Skip to content

[FEAT] Resolve Key Vault-Backend Environment References - #2363

Open
Victor Valbuena (ValbuenaVC) wants to merge 20 commits into
microsoft:mainfrom
ValbuenaVC:env-refactor
Open

[FEAT] Resolve Key Vault-Backend Environment References#2363
Victor Valbuena (ValbuenaVC) wants to merge 20 commits into
microsoft:mainfrom
ValbuenaVC:env-refactor

Conversation

@ValbuenaVC

@ValbuenaVC Victor Valbuena (ValbuenaVC) commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds Key Vault-backed dotenv bootstrapping while preserving PyRIT's existing python-dotenv workflow and list-shaped env_akv_ref configuration.

env_akv_ref:
  - https://my-vault.vault.azure.net/secrets/pyrit-env
env_akv_strict: true

Every URL in env_akv_ref is treated as a bootstrap dotenv document and loaded in list order. A bootstrap document can use native ${NAME} interpolation and complete-value references to scalar secrets in the same vault:

ENDPOINT="https://example.openai.azure.com/openai/v1"
MODEL="${PYRIT_MODEL}"
API_KEY="kv:https://my-vault.vault.azure.net/secrets/api-key"
PINNED_KEY="kv:https://my-vault.vault.azure.net/secrets/api-key/version-id"

kv: is canonical; akv:, azure_key_vault:, and env_akv_ref: remain compatibility aliases. Child-secret values are terminal and are not interpreted as further references.

Source Precedence

Sources load sequentially in this order:

  1. Existing process environment
  2. Key Vault bootstrap documents in env_akv_ref order
  3. Explicit env_files in order, or the defaults:
    • ~/.pyrit/.env
    • ~/.pyrit/.env.local

Every source uses python-dotenv's standard override=True and ${NAME} interpolation behavior. Later assignments override earlier assignments, and interpolation follows assignment and file order. If no Key Vault or local files are configured, PyRIT continues with the existing process environment.

Key Vault child references are resolved only in bootstrap documents. A kv: value in a local file remains literal. Bootstrap references are fetched before later bootstrap documents or local files load, so a reference may be fetched even when a later source overrides it.

Loading intentionally retains historical non-transactional dotenv behavior. Each source updates os.environ as it loads; if a later source or child-secret lookup fails, earlier assignments remain.

Key Vault Validation

References must occupy the complete value and use a full HTTPS secret URL from the bootstrap document's vault. Versionless URLs retrieve the latest version; versioned URLs pin a version. Short names, cross-vault references, arbitrary hosts, credentials, ports, queries, fragments, and malformed secret paths are rejected before client creation.

Supported vault DNS suffixes are:

  • .vault.azure.net
  • .vault.azure.cn
  • .vault.usgovcloudapi.net

env_akv_strict applies only to bootstrap documents:

  • true rejects malformed or valueless entries before that document is loaded.
  • false warns, removes invalid entries, and loads valid assignments.
  • Empty assignments such as NAME= and child secrets containing an empty string remain valid.
  • Local files retain python-dotenv's permissive parsing.

Key Vault clients use an asynchronous retry policy with up to three retries and exponential backoff. Bootstrap, authentication, authorization, transport, malformed-document, and child-secret failures raise KeyVaultInitializationException, preserve the original cause, and remain ValueError-compatible.

Bootstrap documents remain in memory and are never written to disk. Child secrets are not cached; each complete-value reference performs a Key Vault read.

Tests and Documentation

Expanded setup coverage verifies list-shaped configuration, source precedence, assignment-order interpolation, strict and non-strict parsing, terminal child values, versioned URLs, same-vault restrictions, sovereign-cloud endpoints, pre-authentication host rejection, non-transactional failures, retry configuration, exception chaining, and async resource cleanup.

Updated .pyrit_conf_example and the getting-started guide to document the same behavior.

Local Validation

  • Setup unit tests: 160 passed
  • Ruff: passed

The full unit suite, integration tests, Playwright/E2E tests, documentation build, and JupyText checks were not rerun locally after this simplification.

Comment thread doc/getting_started/pyrit_conf.md Outdated
Comment thread doc/getting_started/pyrit_conf.md Outdated
Comment thread .pyrit_conf_example Outdated
Comment thread pyrit/setup/initialization.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds Azure Key Vault-backed environment bootstrapping with recursive reference resolution, precedence handling, warnings, and documentation.

Changes:

  • Resolves env:, kv:, aliases, and escaped literals.
  • Adds environment-source validation and AKV/local-file precedence.
  • Expands tests and configuration documentation.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
pyrit/setup/initialization.py Implements AKV loading and reference resolution.
tests/unit/setup/test_initialization.py Tests environment initialization behavior.
doc/getting_started/pyrit_conf.md Documents loading precedence and AKV references.
.pyrit_conf_example Updates example AKV configuration guidance.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pyrit/setup/initialization.py Outdated
Comment thread tests/unit/setup/test_initialization.py Outdated
Comment thread doc/getting_started/pyrit_conf.md Outdated
@ValbuenaVC Victor Valbuena (ValbuenaVC) changed the title [DRAFT] FEAT Resolve Key Vault-backed environment references [FEAT] Resolve Key Vault-Backend Environment References Aug 12, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Suppressed comments (5)

tests/unit/setup/test_initialization.py:380

  • This patch target is no longer called by initialize_pyrit_async, leaving the output assertion dependent on any real default environment files. Patch _resolve_environment_files instead so unrelated local files cannot add output or trigger reference resolution.
    @mock.patch("pyrit.setup.initialization._load_environment_files", return_value=True)

pyrit/setup/initialization.py:537

  • Direct callers using the former list-shaped env_akv_ref reach .strip() here and get AttributeError, rather than the deliberate ValueError used by ConfigurationLoader. Validate the runtime type before calling string methods so this public API rejects legacy values consistently.
        if not env_akv_ref.strip():

pyrit/setup/initialization.py:439

  • This merge is case-sensitive even on Windows. For example, ambient Path=old plus a winning PATH=new leaves both keys, so env:Path returns the ambient exact match and violates the documented merged-source precedence. Normalize keys on Windows while applying values last.
    reference_environment = {**ambient_environment, **values}

tests/unit/setup/test_initialization.py:372

  • initialize_pyrit_async no longer calls _load_environment_files, so this patch is inert and the test can read real ~/.pyrit files (and even resolve their Key Vault references). Patch the resolver now used by initialization to keep the unit test isolated.

This issue also appears on line 380 of the same file.

    @mock.patch("pyrit.setup.initialization._load_environment_files", return_value=True)

doc/getting_started/pyrit_conf.md:174

  • This row contradicts both the implementation and the earlier AKV precedence section: when env_files is omitted, initialization loads both .env and .env.local after the bootstrap. Remove the claim that only .env.local is loaded.
| Omitted or `null` | Load default `~/.pyrit/.env` and `~/.pyrit/.env.local`, or only `.env.local` after an AKV root |

Comment thread pyrit/setup/initialization.py Outdated
Comment thread pyrit/setup/initialization.py Outdated
Comment thread pyrit/setup/configuration_loader.py Outdated
@ValbuenaVC
Victor Valbuena (ValbuenaVC) marked this pull request as ready for review August 13, 2026 14:10
Comment thread pyrit/setup/initialization.py Outdated
Comment thread pyrit/setup/initialization.py Outdated
Victor Valbuena added 2 commits August 13, 2026 12:53
Comment thread doc/getting_started/pyrit_conf.md Outdated
Comment thread .pyrit_conf_example Outdated
Co-authored-by: Justin Song <songjustin@microsoft.com>
…to env-refactor

Merged in changes from main.
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.

5 participants