Security posture
The Security posture screen lets you read the protections of the platform and its automated pass, warn and fail checks, change the security settings of your tenant (prompt injection, untrusted content, links, egress) and follow the recent security events. Portal path: /security/posture, menu Security and governance > Security posture. It is used by security officers (security_officer) and tenant administrators (tenant_admin).
What the screen shows
The screen is built by portal/src/screens/security-posture.tsx and reads four routes.
- Header line: the Environment badge (for example
prod) and the Generated date of the posture, both fromGET /v1/security/posture. - Summary cards: three counters, Passing, Warnings and Failing (field
summaryof the posture). - Checks card: the checks grouped by category, sorted by name. Each line shows a status badge (Pass, Warn or Fail), the message in the language of the portal (
message_enormessage_fr), the identifier of the check in monospace and, when present, a severity badge (Critical, High, Medium, Low). Without any check: "No check reported". - Protections card: one tile per protection of the platform (for example Security headers, HSTS, Allowed hosts, Trusted proxies, Body limits, Rate limits, Authentication, CORS origins, Egress policy, API docs exposed). A boolean shows Enabled or Disabled, a list shows one badge per value, an object is shown as JSON. Without any protection: "No protection reported".
- Tenant security settings card ("Partial update, audited as security.settings.update."), loaded by
GET /v1/security/settings:
| Field | Values | Default | Meaning |
|---|---|---|---|
| Prompt injection mode | Off, Detect, Block | Detect | off: no check, detect: audit only, block: refuse the request. |
| Links in model output | Allow, Strip untrusted | Strip untrusted | strip_untrusted removes links to hosts outside the allowlist. |
| Wrap untrusted content | switch | on | Tool results and documents are delimited as untrusted data. |
| Allowed link hosts | one host per line | empty | Hosts whose links stay in the model output. |
| Allowed egress hosts | one host or *.suffix per line | empty | Hosts the tenant may call. Empty: no tenant restriction. |
- Recent security events card: a table with the columns When, Action, Actor and Outcome. The portal reads the last 200 audit events (
GET /v1/audit/events) and keeps the 50 latest whose action starts withsecurity.. Without any event: "No security event".
Actions
| Button or action | What it does | API call | Roles |
|---|---|---|---|
| Refresh (page header) | Reads the posture again: summary, checks and protections. | GET /v1/security/posture | security_officer, tenant_admin |
| Retry (error state of a card) | Reads the failed card again. | The route of the card | Same as the card |
| Save settings | Sends the five settings of the form as a partial update, then reloads the posture and the events. Message "Security settings saved". | PUT /v1/security/settings | tenant_admin, security_officer |
| Reset | Puts the form back to the values last loaded from the API, without any call. | None | Every reader of the screen |
| Loading of Tenant security settings | Reads the current settings of the tenant. | GET /v1/security/settings | tenant_admin, security_officer |
| Loading of Recent security events | Reads the audit events and filters the security.* actions. | GET /v1/audit/events | auditor, security_officer, tenant_admin, platform_admin |
Roles
The portal hides no part of this screen: the Control Plane decides. On the server side (services/control-plane/src/eai/domains/security/__init__.py), the posture and the settings both require security_officer or tenant_admin, and platform_admin passes every role check. The settings need a tenant: a principal without tenant gets tenant_required. The events card follows the readers of the audit log (ROLES.auditReaders in portal/src/lib/kit.ts: auditor, security_officer, tenant_admin, platform_admin). Every save is written in the audit log as security.settings.update with the changed fields.
Typical tasks
Review the posture before a go live
- Open the screen. The posture gathers the checks of the platform in one place. Menu Security and governance > Security posture. Expected result: the header shows the environment and the generation date, then the three counters. If it fails:
forbidden(HTTP 403) means you hold neithersecurity_officernortenant_admin. - Read the failing checks first. A Fail check is a protection that is missing or misconfigured. Look in the Checks card for the Fail badges and note their identifier. Expected result: the list of the checks to fix, with their severity. If it fails: "No check reported" means the API returned an empty list; click Refresh.
- Check the protections. They show the effective configuration of the API (headers, hosts, limits, CORS). Read the Protections card. Expected result: Enabled on the protections you expect, the allowed hosts and CORS origins of your deployment. If it fails: a protection shown Disabled is changed in the configuration of the Control Plane, not from this screen (see the security configuration page).
- Refresh after a fix. The posture is computed on each read. Click Refresh. Expected result: the counter Failing goes down. If it fails: the configuration change was not applied to the running Control Plane.
Block prompt injections for the tenant
- Choose the mode.
blockrefuses a request whose content looks like an injection,detectonly audits it. In Tenant security settings, set Prompt injection mode to Block. Expected result: the select shows Block. If it fails: the card shows "Sign in with a tenant to edit its settings." when the API answers 400 or 404; sign in with an account of the tenant. - Keep the untrusted content wrapped. Wrapping delimits tool results and documents as data, not as instructions. Leave Wrap untrusted content on. Expected result: the switch is on.
- Save. Click Save settings. Expected result: the message "Security settings saved", and a
security.settings.updateline in Recent security events. If it fails:forbiddenmeans a missing role; an error under a field means the value of that field was refused.
Restrict the hosts the tenant can call
- List the hosts. Only public host names are accepted, one per line, or
*.suffixfor a whole domain. Fill Allowed egress hosts, for exampleapi.openai.com. Expected result: one host per line in the field. If it fails: nothing to do at this step. - Save. Click Save settings. Expected result: "Security settings saved". If it fails: "Invalid public host name" under the field means an IP address,
localhostor an internal suffix (.local,.internal,.svc,.corp...) was entered; "Expected a list of at most 200 host names." means the list is too long. - Verify. An outbound call to another host is now refused. Watch Recent security events. Expected result: the egress denials appear there. If it fails: click Retry on the events card;
forbiddenon this card means you are not a reader of the audit log.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Error state with forbidden (HTTP 403) on the posture | Your roles are neither security_officer nor tenant_admin. | Ask a tenant administrator for one of these roles. |
| "Sign in with a tenant to edit its settings." | GET /v1/security/settings answered 400 or 404 (for example unknown_tenant). | Sign in with an account attached to a tenant. |
Error tenant_required in the settings card | The principal acts without a tenant (HTTP 403). | Use an account or an API key of the tenant. |
| Red message under a field after Save settings | validation_error (HTTP 422) with details.field: unknown setting, value outside off, detect, block or allow, strip_untrusted, invalid public host name, more than 200 hosts. | Fix the field named in the message and save again. |
Recent security events shows forbidden | GET /v1/audit/events needs auditor, security_officer, tenant_admin or platform_admin. | Ask for a role that reads the audit log. |
| "No security event" | No audit event with an action security.* among the last 200 events. | Normal on a quiet tenant; see the full log on the audit events screen. |
| "The Control Plane API cannot be reached" | The API is down or the API address of the portal is wrong. | Check the Control Plane and the NEXT_PUBLIC_EAI_API_URL setting of the portal. |