Skip to content

Commit 293a4ac

Browse files
committed
fix(telemetry): resolve F-07 enforce private identity export
1 parent fe35a42 commit 293a4ac

5 files changed

Lines changed: 50 additions & 4 deletions

File tree

docs/architecture.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ The Foundry call is isolated behind the `WorkflowAgentService` protocol. This ke
3030
- `cas.workflow.execute` covers core orchestration.
3131
- `foundry.responses.create` covers the external Foundry call.
3232
- CAS correlation IDs are attached to workflow spans and canonical events preserve W3C trace context.
33-
33+
- Broad Azure SDK and outbound HTTP auto-instrumentation is disabled to avoid capturing prompt or
34+
output content. The application records only explicit boundary spans and safe identifiers.

docs/operations.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ Set `ENVIRONMENT` to `dev`, `test`, or `prod`; set `WORKFLOW_BACKEND=foundry`; p
1515

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

18+
When `APPLICATIONINSIGHTS_CONNECTION_STRING` is supplied, telemetry export also authenticates with
19+
the environment credential. Grant the system-assigned identity the minimum Azure Monitor publishing
20+
role required by the deployment. Retry-file storage and broad outbound HTTP/SDK auto-instrumentation
21+
are disabled; explicit spans do not record prompt or output content.
22+
1823
## Platform Handoff
1924

2025
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.
21-

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.

src/cas_reference_product/telemetry.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from opentelemetry import trace
22

33
from .config import Settings
4+
from .identity import build_credential
45

56

67
def configure_telemetry(settings: Settings) -> None:
@@ -9,6 +10,14 @@ def configure_telemetry(settings: Settings) -> None:
910

1011
configure_azure_monitor(
1112
connection_string=settings.applicationinsights_connection_string,
13+
credential=build_credential(settings.environment),
14+
disable_offline_storage=True,
15+
instrumentation_options={
16+
"azure_sdk": {"enabled": False},
17+
"requests": {"enabled": False},
18+
"urllib": {"enabled": False},
19+
"urllib3": {"enabled": False},
20+
},
1221
service_name=settings.app_name,
1322
)
1423

tests/test_telemetry.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,37 @@
55

66

77
def test_telemetry_is_noop_without_application_insights() -> None:
8-
configure_telemetry(Settings())
8+
with patch("cas_reference_product.telemetry.build_credential") as credential:
9+
configure_telemetry(Settings())
10+
credential.assert_not_called()
11+
12+
13+
def test_telemetry_uses_identity_and_privacy_hardening() -> None:
14+
settings = Settings(
15+
environment="prod",
16+
applicationinsights_connection_string=(
17+
"InstrumentationKey=00000000-0000-0000-0000-000000000000"
18+
),
19+
)
20+
with (
21+
patch("cas_reference_product.telemetry.build_credential") as credential,
22+
patch("azure.monitor.opentelemetry.configure_azure_monitor") as configure,
23+
):
24+
configure_telemetry(settings)
25+
26+
credential.assert_called_once_with("prod")
27+
configure.assert_called_once_with(
28+
connection_string=settings.applicationinsights_connection_string,
29+
credential=credential.return_value,
30+
disable_offline_storage=True,
31+
instrumentation_options={
32+
"azure_sdk": {"enabled": False},
33+
"requests": {"enabled": False},
34+
"urllib": {"enabled": False},
35+
"urllib3": {"enabled": False},
36+
},
37+
service_name=settings.app_name,
38+
)
939

1040

1141
def test_invalid_span_preserves_incoming_traceparent() -> None:

0 commit comments

Comments
 (0)