Skip to content

Commit 56bd0e6

Browse files
adrian-gavrilaromanlutzAdrian GavrilaCopilot
authored
FEAT: Security & Azure deployment for CoPyRIT GUI (#1554)
Co-authored-by: Roman Lutz <romanlutz@users.noreply.github.com> Co-authored-by: Adrian Gavrila <agavrila@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7e81131 commit 56bd0e6

31 files changed

Lines changed: 3159 additions & 43 deletions

.devcontainer/Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
5454
libpulse0
5555

5656
# Install uv system-wide and create pyrit-dev venv
57-
COPY --from=ghcr.io/astral-sh/uv:0.10.8 /uv /uvx /bin/
57+
RUN curl -LsSf https://astral.sh/uv/0.10.8/install.sh | sh \
58+
&& mv /root/.local/bin/uv /bin/uv \
59+
&& mv /root/.local/bin/uvx /bin/uvx
5860
RUN uv venv /opt/venv --python 3.11 --prompt pyrit-dev \
5961
&& chown -R vscode:vscode /opt/venv
6062
ENV PATH="/opt/venv/bin:$PATH"

build_scripts/prepare_package.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def build_frontend(frontend_dir: Path) -> bool:
4848
print("\nInstalling frontend dependencies...")
4949
try:
5050
subprocess.run(
51-
[npm, "install"],
51+
["npm", "install", "--legacy-peer-deps"],
5252
cwd=frontend_dir,
5353
check=True,
5454
stdout=subprocess.PIPE,

docker/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
# python docker/build_pyrit_docker.py --source pypi --version 0.10.0
1111
# ============================================================================
1212

13-
# Use the devcontainer as base (built by build_pyrit_docker.py)
14-
ARG BASE_IMAGE=pyrit-devcontainer
13+
# No default — callers must pass --build-arg BASE_IMAGE=... (see infra/README.md).
14+
ARG BASE_IMAGE
1515
FROM ${BASE_IMAGE} AS production
1616

1717
LABEL description="Docker container for PyRIT with Jupyter Notebook and GUI support"

docker/QUICKSTART.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,19 @@ Docker container for PyRIT with support for both **Jupyter Notebook** and **GUI*
44

55
## Prerequisites
66
- Docker installed and running
7-
- `.env` file at `~/.pyrit/.env` with API keys
8-
- Optionally, `~/.pyrit/.env.local` for additional environment variables
7+
- `~/.pyrit/.env` with your API keys
8+
- `~/.pyrit/.pyrit_conf` with your configuration (operator, operation, initializers)
9+
- Optionally, `~/.pyrit/.env.local` for additional environment overrides
10+
11+
## Azure Authentication in Docker
12+
13+
When deployed to **Azure infrastructure** (AKS, ACI, Azure VM), managed identity
14+
works out of the box — no configuration needed. Assign the managed identity the
15+
**Cognitive Services OpenAI User** role on your Azure OpenAI resources.
16+
17+
> **Note:** Azure authentication for local Docker Desktop is not yet supported.
18+
> Local Docker is currently limited to targets that use API keys configured in
19+
> your `.env` file.
920
1021
## Quick Start
1122

@@ -41,6 +52,11 @@ GUI mode (port 8000):
4152
python docker/run_pyrit_docker.py gui
4253
```
4354

55+
The run script automatically mounts these files from `~/.pyrit/`:
56+
- `.env` — API keys (required)
57+
- `.env.local` — Additional environment overrides (optional)
58+
- `.pyrit_conf` — PyRIT configuration: operator, operation, initializers (optional)
59+
4460
## Image Tags
4561

4662
Images are tagged with version information:
@@ -77,6 +93,9 @@ docker-compose --profile gui up
7793

7894
**.env missing**: Create `.env` file at `~/.pyrit/.env` with your API keys
7995

96+
**Azure auth fails in container**: Local Docker Desktop does not currently support
97+
Azure token-based authentication. Use API key-based targets instead.
98+
8099
**GUI frontend missing**: Build with `--source local` (PyPI builds before GUI release won't work)
81100

82101
For complete documentation, see [docker/README.md](./README.md)

docker/docker-compose.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ services:
4949
- ../assets:/app/assets
5050
- ~/.pyrit/.env:/home/vscode/.pyrit/.env:ro
5151
- ~/.pyrit/.env.local:/home/vscode/.pyrit/.env.local:ro
52+
- ~/.pyrit/.pyrit_conf:/home/vscode/.pyrit/.pyrit_conf:ro
53+
- ~/.azure:/home/vscode/.azure
5254
environment:
5355
- PYRIT_MODE=gui
5456
restart: unless-stopped

docker/run_pyrit_docker.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def run_container(mode, tag="latest"):
2828
pyrit_config_dir = Path.home() / ".pyrit"
2929
env_file = pyrit_config_dir / ".env"
3030
env_local_file = pyrit_config_dir / ".env.local"
31+
pyrit_conf_file = pyrit_config_dir / ".pyrit_conf"
3132

3233
print("🐳 PyRIT Docker Runner")
3334
print("=" * 60)
@@ -69,10 +70,12 @@ def run_container(mode, tag="latest"):
6970

7071
# Build docker run command
7172
# Mount env files to ~/.pyrit/ where PyRIT expects them
73+
# Use -it for interactive mode (needed for az login device code prompt)
7274
cmd = [
7375
"docker",
7476
"run",
7577
"--rm",
78+
"-it",
7679
"--name",
7780
container_name,
7881
"-p",
@@ -88,6 +91,19 @@ def run_container(mode, tag="latest"):
8891
print(f" Found .env.local - including it")
8992
cmd.extend(["-v", f"{env_local_file}:/home/vscode/.pyrit/.env.local:ro"])
9093

94+
# Add .pyrit_conf if it exists (sets operator, operation, initializers)
95+
if pyrit_conf_file.exists():
96+
print(f" Found .pyrit_conf - including it")
97+
cmd.extend(["-v", f"{pyrit_conf_file}:/home/vscode/.pyrit/.pyrit_conf:ro"])
98+
99+
# Mount Azure CLI config so 'az login' tokens persist across container restarts.
100+
# ~/.azure is created by the Azure CLI when you run 'az login' on the host.
101+
# Mounting it lets the container reuse existing auth tokens without re-login.
102+
azure_dir = Path.home() / ".azure"
103+
if azure_dir.exists():
104+
print(f" Found .azure/ - mounting for Azure CLI auth")
105+
cmd.extend(["-v", f"{azure_dir}:/home/vscode/.azure"])
106+
91107
cmd.append(f"pyrit:{tag}")
92108

93109
print()

docker/start.sh

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ fi
3737
echo "Checking PyRIT installation..."
3838
python -c "import pyrit; print(f'Running PyRIT version: {pyrit.__version__}')"
3939

40+
# Write .env file from PYRIT_ENV_CONTENTS (injected from Key Vault secret)
41+
if [ -n "$PYRIT_ENV_CONTENTS" ]; then
42+
mkdir -p ~/.pyrit
43+
echo "$PYRIT_ENV_CONTENTS" > ~/.pyrit/.env
44+
echo "Wrote .env file from PYRIT_ENV_CONTENTS ($(wc -l < ~/.pyrit/.env) lines)"
45+
else
46+
echo "No PYRIT_ENV_CONTENTS set — using system environment variables only"
47+
fi
48+
4049
# Start the appropriate service based on PYRIT_MODE
4150
if [ "$PYRIT_MODE" = "jupyter" ]; then
4251
echo "Starting JupyterLab on port 8888..."
@@ -45,7 +54,25 @@ if [ "$PYRIT_MODE" = "jupyter" ]; then
4554
exec jupyter lab --ip=0.0.0.0 --port=8888 --no-browser --allow-root --notebook-dir=/app/notebooks
4655
elif [ "$PYRIT_MODE" = "gui" ]; then
4756
echo "Starting PyRIT GUI on port 8000..."
48-
exec python -m uvicorn pyrit.backend.main:app --host 0.0.0.0 --port 8000
57+
# Use Azure SQL if AZURE_SQL_SERVER is set (injected by Bicep), otherwise default to SQLite.
58+
# Note: AZURE_SQL_DB_CONNECTION_STRING is in the .env file (loaded by Python dotenv),
59+
# but we use AZURE_SQL_SERVER here because it's a direct env var from the Bicep template.
60+
# Build CLI arguments
61+
BACKEND_ARGS="--host 0.0.0.0 --port 8000"
62+
63+
if [ -n "$AZURE_SQL_SERVER" ]; then
64+
echo "Using Azure SQL database (server: $AZURE_SQL_SERVER)"
65+
BACKEND_ARGS="$BACKEND_ARGS --database AzureSQL"
66+
else
67+
echo "Using SQLite database (AZURE_SQL_SERVER not set)"
68+
fi
69+
70+
if [ -n "$PYRIT_INITIALIZER" ]; then
71+
echo "Using initializer: $PYRIT_INITIALIZER"
72+
BACKEND_ARGS="$BACKEND_ARGS --initializers $PYRIT_INITIALIZER"
73+
fi
74+
75+
exec python -m pyrit.cli.pyrit_backend $BACKEND_ARGS
4976
else
5077
echo "ERROR: Invalid PYRIT_MODE '$PYRIT_MODE'. Must be 'jupyter' or 'gui'"
5178
exit 1

frontend/.npmrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Pin npm registry — satisfies CSSC CFS0001 (sibling .npmrc required).
2+
registry=https://registry.npmjs.org/
3+
legacy-peer-deps=true

frontend/package-lock.json

Lines changed: 45 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
"test:e2e:headed": "playwright test --headed"
2323
},
2424
"dependencies": {
25+
"@azure/msal-browser": "^5.5.0",
26+
"@azure/msal-react": "^5.0.7",
2527
"@fluentui/react-components": "9.72.7",
2628
"@fluentui/react-icons": "2.0.258",
2729
"axios": "1.14.0",
@@ -32,6 +34,7 @@
3234
"devDependencies": {
3335
"@eslint/js": "9.39.1",
3436
"@playwright/test": "1.58.0",
37+
"@testing-library/dom": "^10.4.1",
3538
"@testing-library/jest-dom": "6.9.1",
3639
"@testing-library/react": "16.3.2",
3740
"@testing-library/user-event": "14.6.1",
@@ -49,6 +52,7 @@
4952
"identity-obj-proxy": "3.0.0",
5053
"jest": "29.7.0",
5154
"jest-environment-jsdom": "29.7.0",
55+
"scheduler": "^0.27.0",
5256
"ts-jest": "29.4.6",
5357
"ts-node": "10.9.2",
5458
"typescript": "5.9.3",

0 commit comments

Comments
 (0)