Skip to content

Commit f09c271

Browse files
authored
Merge pull request #1 from Coding-Autopilot-System/audit/v0.1-integration-hardening
fix: harden v0.1 Foundry integration and contracts
2 parents 371c98f + 42a5744 commit f09c271

29 files changed

Lines changed: 820 additions & 42 deletions

.dockerignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
.git
22
.github
33
.planning
4+
.env
5+
.coverage
46
.venv
57
__pycache__
68
.pytest_cache
9+
.mypy_cache
710
.ruff_cache
11+
*.py[cod]
812
tests
913
docs
10-

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ Dockerfile text eol=lf
1010
*.md text eol=lf
1111
*.toml text eol=lf
1212
*.ps1 text eol=crlf
13+
tests/contracts/cas-contracts/v0.1.0/*.json -text -diff

Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ FROM python:3.12-slim AS runtime
22

33
ENV PYTHONDONTWRITEBYTECODE=1 \
44
PYTHONUNBUFFERED=1 \
5+
PIP_DISABLE_PIP_VERSION_CHECK=1 \
56
PORT=8080
67

78
WORKDIR /app
@@ -10,12 +11,12 @@ RUN addgroup --system app && adduser --system --ingroup app app
1011

1112
COPY pyproject.toml README.md ./
1213
COPY src ./src
13-
RUN pip install --no-cache-dir .
14+
RUN pip install --no-cache-dir --no-compile .
1415

1516
USER app
1617
EXPOSE 8080
18+
STOPSIGNAL SIGTERM
1719
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
1820
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/health/live', timeout=2)"
1921

2022
CMD ["uvicorn", "cas_reference_product.app:app", "--host", "0.0.0.0", "--port", "8080"]
21-

deployment/cas-platform.interface.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,3 @@ spec:
2323
- FOUNDRY_AGENT_NAME
2424
deploymentInjected:
2525
- APPLICATIONINSIGHTS_CONNECTION_STRING
26-
outputsConsumed:
27-
- workloadPrincipalId
28-
- applicationInsightsId
29-
- logAnalyticsWorkspaceId
30-

docs/architecture.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@ The Foundry call is isolated behind the `WorkflowAgentService` protocol. This ke
2222

2323
## Deployment Interface
2424

25-
`deployment/cas-platform.interface.yaml` records the contract: Linux AMD64 image, port 8080, internal ingress by default, system-assigned identity, probes, non-secret identifiers, and platform outputs. It does not deploy resources.
25+
`deployment/cas-platform.interface.yaml` records the application contract: Linux AMD64 image, port
26+
8080, internal ingress by default, system-assigned identity, probes, and configuration inputs. It does
27+
not deploy resources.
28+
29+
The application consumes only the environment values listed in that interface. Platform resource IDs
30+
and principal IDs remain deployment-orchestration outputs and are not application configuration.
2631

2732
## Observability Boundaries
2833

2934
- Incoming HTTP requests are instrumented by Azure Monitor OpenTelemetry when configured.
3035
- `cas.workflow.execute` covers core orchestration.
3136
- `foundry.responses.create` covers the external Foundry call.
3237
- CAS correlation IDs are attached to workflow spans and canonical events preserve W3C trace context.
33-
38+
- Broad Azure SDK and outbound HTTP auto-instrumentation is disabled to avoid capturing prompt or
39+
output content. The application records only explicit boundary spans and safe identifiers.

docs/operations.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,28 @@
99

1010
Submit `examples/prompt-envelope.json` to `POST /api/v1/workflows`. Liveness is `/health/live`; readiness is `/health/ready`.
1111

12+
For a hardened local container run:
13+
14+
```powershell
15+
docker run --rm --read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges `
16+
-p 8080:8080 cas-reference-product:local
17+
```
18+
1219
## Foundry Mode
1320

1421
Set `ENVIRONMENT` to `dev`, `test`, or `prod`; set `WORKFLOW_BACKEND=foundry`; provide the non-secret `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_AGENT_NAME`. The Azure-hosted workload uses its system-assigned managed identity. Do not configure API keys or client secrets.
1522

1623
Readiness fails until required Foundry identifiers are present. Foundry connectivity is exercised only by workflow requests, not probes.
1724

25+
When `APPLICATIONINSIGHTS_CONNECTION_STRING` is supplied, telemetry export also authenticates with
26+
the environment credential. Grant the system-assigned identity the minimum Azure Monitor publishing
27+
role required by the deployment. Retry-file storage and broad outbound HTTP/SDK auto-instrumentation
28+
are disabled; explicit spans do not record prompt or output content.
29+
1830
## Platform Handoff
1931

2032
Build a Linux AMD64 image and pass its immutable image reference to the `containerImage` parameter of `cas-platform`. Review `deployment/cas-platform.interface.yaml` before platform changes. This repository intentionally contains no Azure deployment command.
2133

34+
The Docker build context excludes local `.env` files and development artifacts. The application does
35+
not consume platform resource IDs; deployment orchestration retains those outputs for RBAC and
36+
operations workflows.

docs/threat-model.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
| Credential disclosure | No keys or tokens in code; system-assigned `ManagedIdentityCredential` in Azure | Operators must maintain least-privilege RBAC |
2222
| Legacy API use | Adapter uses project Responses client with `agent_reference`; no Classic Assistants code | SDK behavior must be reviewed during upgrades |
2323
| Prompt injection | Workflow treats prompts as untrusted data and exposes no tools in v0.1 | Downstream agent policy remains product-specific |
24-
| Sensitive telemetry | Events contain identifiers and status, not prompt text or agent output | Operators must review SDK and platform log settings |
24+
| Sensitive telemetry | Explicit spans contain safe identifiers only; broad outbound HTTP/SDK auto-instrumentation and retry-file storage are disabled | Operators must review platform log settings |
2525
| Unauthorized invocation | External ingress disabled by default in platform interface | Authentication gateway is product-specific and out of scope |
2626
| Supply-chain compromise | Pinned CI actions, lint, tests, non-root container | Dependency update review remains required |
2727
| Denial of service | Platform scaling bounds and request validation | Product-specific quotas and rate limits are not included |
@@ -30,3 +30,5 @@
3030

3131
Grant the Container App system-assigned identity only the minimum Foundry project role needed to invoke the selected agent, scoped to the narrowest resource. Do not assign subscription-wide roles.
3232

33+
When Application Insights export is enabled, also grant the minimum Azure Monitor publishing role
34+
required by the deployment at the narrowest telemetry resource scope.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies = [
2222
[project.optional-dependencies]
2323
dev = [
2424
"httpx>=0.28.0",
25+
"jsonschema>=4.23.0",
2526
"mypy>=1.14.0",
2627
"pytest>=8.3.0",
2728
"pytest-cov>=6.0.0",

src/cas_reference_product/app.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .config import Settings, get_settings
88
from .models import PromptEnvelope, WorkflowResult
99
from .telemetry import configure_telemetry
10-
from .workflow import WorkflowOrchestrator, build_workflow_agent_service
10+
from .workflow import WorkflowAgentServiceError, WorkflowOrchestrator, build_workflow_agent_service
1111

1212

1313
def create_app(settings: Settings | None = None) -> FastAPI:
@@ -37,7 +37,10 @@ def execute(envelope: PromptEnvelope, request: Request) -> WorkflowResult:
3737
raise HTTPException(status_code=503, detail="Workflow backend is not ready")
3838
request.state.correlation_id = envelope.correlationId
3939
orchestrator = WorkflowOrchestrator(service, app_settings.repository)
40-
return orchestrator.execute(envelope)
40+
try:
41+
return orchestrator.execute(envelope)
42+
except WorkflowAgentServiceError:
43+
raise HTTPException(status_code=502, detail="Workflow backend request failed") from None
4144

4245
@app.get("/")
4346
def root() -> dict[str, Any]:

src/cas_reference_product/config.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
from functools import lru_cache
23
from typing import Literal
34

@@ -17,11 +18,21 @@ class Settings(BaseSettings):
1718
applicationinsights_connection_string: str | None = Field(default=None, repr=False)
1819

1920
@property
20-
def ready(self) -> bool:
21-
return self.workflow_backend == "local" or bool(
22-
self.foundry_project_endpoint and self.foundry_agent_name
21+
def foundry_ready(self) -> bool:
22+
return bool(
23+
self.foundry_project_endpoint
24+
and re.fullmatch(
25+
r"https://[A-Za-z0-9.-]+\.services\.ai\.azure\.com/api/projects/[A-Za-z0-9_.-]+/?",
26+
self.foundry_project_endpoint,
27+
)
28+
and self.foundry_agent_name
29+
and self.foundry_agent_name.strip()
2330
)
2431

32+
@property
33+
def ready(self) -> bool:
34+
return self.workflow_backend == "local" or self.foundry_ready
35+
2536

2637
@lru_cache
2738
def get_settings() -> Settings:

0 commit comments

Comments
 (0)