diff --git a/product/admin/expressions-examples.mdx b/product/admin/expressions-examples.mdx index 6f51989a..0170d8a2 100644 --- a/product/admin/expressions-examples.mdx +++ b/product/admin/expressions-examples.mdx @@ -616,5 +616,6 @@ The `appOwners` variable is always available in policy step expressions and neve - **[CEL expressions reference](/product/admin/expressions-reference)** - Complete reference for all available objects, functions, and time functions - **[Workflow expressions](/product/admin/expressions-workflows)** - Pass data between automation steps using the ctx object - **[Troubleshooting expressions](/product/admin/expressions-troubleshooting)** - Debug common errors and understand failure modes +- **[Use external insights in CEL policy conditions](/product/admin/external-insights#use-external-insights-in-cel-policy-conditions)** - Reference security findings and risk scores from Wiz and CrowdStrike in access decisions diff --git a/product/admin/expressions-reference.mdx b/product/admin/expressions-reference.mdx index 0bb6af8b..85257e06 100644 --- a/product/admin/expressions-reference.mdx +++ b/product/admin/expressions-reference.mdx @@ -389,6 +389,35 @@ time.format(now(), TimeFormat.DATETIME, "Europe/London") The `now()` function returns the same value throughout a single expression evaluation. All timestamps are stored in UTC internally; timezone parameters only affect parsing and formatting. +### External insights functions + +These functions are available in policy conditions when [external insights](/product/admin/external-insights) are configured. They let you reference security findings and risk scores from connected tools directly in access decisions. + +#### Issue insights + +Issue insights are discrete security findings (for example, critical CVEs or misconfigurations) synced from Wiz Insights. + +| Function | Accepts | Returns | Availability | +|:----------|:---------|:---------|:--------------| +| `c1.app_user.v1.HasSecurityInsight(account)` | account | `bool` | Policy conditions | +| `c1.app_user.v1.GetSecurityInsights(account, source)` | account, source name | `list` | Policy conditions | +| `c1.app_user.v1.HasSecurityInsightWithSeverity(account, source, severity)` | account, source name, severity | `bool` | Policy conditions | + +Severity values: `"CRITICAL"`, `"HIGH"`, `"MEDIUM"`, `"LOW"`, `"INFORMATIONAL"`. The `HasSecurityInsightWithSeverity` comparison is case-insensitive; direct field comparisons are not. + +#### Risk scores + +Risk scores are normalized values (0–100, higher = more risk) synced from CrowdStrike. + +| Function | Accepts | Returns | Availability | +|:----------|:---------|:---------|:--------------| +| `c1.app_user.v1.HasRiskScore(account)` | account | `bool` | Policy conditions | +| `c1.app_user.v1.GetRiskScore(account, source)` | account, source name | `c1.risk_score.v1` | Policy conditions | + +The source name must exactly match the connector app's display name in your tenant (for example, `"Wiz Insights"` or `"CrowdStrike"`). The match is case-sensitive. + +For full examples and best practices, see [Use external insights in CEL policy conditions](/product/admin/external-insights#use-external-insights-in-cel-policy-conditions). + ## Objects ### Subject object diff --git a/product/admin/external-insights.mdx b/product/admin/external-insights.mdx index 0263aac2..616ab6af 100644 --- a/product/admin/external-insights.mdx +++ b/product/admin/external-insights.mdx @@ -59,14 +59,130 @@ The task log includes an **Insights** column. Hovering over the insights indicat Approvers can see an identity's current risk score and risk factors in a request task before submitting their decision. +## Use external insights in CEL policy conditions + +Beyond surfacing risk data in the UI, you can reference external insights directly in [CEL policy conditions](/product/admin/expressions-reference#external-insights-functions) to automate access decisions based on a user's security posture. + +External insights expose two data types through CEL, each from a different connector: + +| Data type | Connector | CEL path | Description | +|-----------|-----------|----------|-------------| +| Issue insights | Wiz Insights | `account.security_insights` | Discrete security findings such as critical CVEs, misconfigurations, and exposed secrets | +| Risk scores | CrowdStrike | `account.risk_score` / `account.risk_scores` | Normalized identity risk scores (0–100, higher = more risk) | + + +All CEL functions that accept a source app name use the **display name** of the connector app in your tenant. The defaults are `"Wiz Insights"` and `"CrowdStrike"`. If you've renamed a connector app, use the renamed name instead. The match is **exact and case-sensitive**. + + +### Issue insights (Wiz) + +Issue insights represent discrete security findings synced from Wiz. Each insight has three fields: `source` (the connector app name), `value` (a description like `"3 Critical CVEs"`), and `severity` (`"CRITICAL"`, `"HIGH"`, `"MEDIUM"`, `"LOW"`, or `"INFORMATIONAL"`). + +**Helper functions** + +| Function | Returns | Description | +|----------|---------|-------------| +| `c1.app_user.v1.HasSecurityInsight(account)` | `bool` | `true` if the account has any issue insights from any source | +| `c1.app_user.v1.GetSecurityInsights(account, source)` | `list` | All issue insights from the named source | +| `c1.app_user.v1.HasSecurityInsightWithSeverity(account, source, severity)` | `bool` | `true` if the account has at least one issue from the named source at the given severity (case-insensitive) | + +**Examples** + +Deny access when critical Wiz findings exist: + +```go +c1.app_user.v1.HasSecurityInsightWithSeverity(account, "Wiz Insights", "CRITICAL") +``` + +Block privileged access on HIGH or CRITICAL Wiz issues: + +```go +c1.app_user.v1.HasSecurityInsightWithSeverity(account, "Wiz Insights", "CRITICAL") + || c1.app_user.v1.HasSecurityInsightWithSeverity(account, "Wiz Insights", "HIGH") +``` + +Auto-approve when no Wiz issues exist: + +```go +c1.app_user.v1.GetSecurityInsights(account, "Wiz Insights").size() == 0 +``` + +### Risk scores (CrowdStrike) + +Risk scores are normalized values from 0 (no risk) to 100 (highest risk). Each score has two fields: `source` (the connector app name) and `normalized_score` (the integer score). + +You can access scores in two ways: +- **Map** — `account.risk_score` is keyed by source display name and returns the score as an integer. +- **List** — `account.risk_scores` returns a list of score objects, useful for cross-source queries. + +**Helper functions** + +| Function | Returns | Description | +|----------|---------|-------------| +| `c1.app_user.v1.HasRiskScore(account)` | `bool` | `true` if the account has risk scores from any source | +| `c1.app_user.v1.GetRiskScore(account, source)` | `c1.risk_score.v1` | The risk score object for the named source (returns `normalized_score` of `0` if missing) | + +**Examples** + +Block access when the CrowdStrike score exceeds a threshold: + +```go +c1.app_user.v1.GetRiskScore(account, "CrowdStrike").normalized_score > 70 +``` + +Require extra approval for elevated risk: + +```go +c1.app_user.v1.HasRiskScore(account) + && c1.app_user.v1.GetRiskScore(account, "CrowdStrike").normalized_score > 50 +``` + +Auto-approve when CrowdStrike reports low risk: + +```go +"CrowdStrike" in account.risk_score && account.risk_score["CrowdStrike"] <= 20 +``` + +Deny access if no CrowdStrike score exists (device may be unmanaged): + +```go +!("CrowdStrike" in account.risk_score) +``` + +### Combining sources + +You can combine Wiz issue insights and CrowdStrike risk scores in a single condition. + +Escalate when CrowdStrike risk is elevated and Wiz has critical findings: + +```go +("CrowdStrike" in account.risk_score && account.risk_score["CrowdStrike"] > 50) + && c1.app_user.v1.HasSecurityInsightWithSeverity(account, "Wiz Insights", "CRITICAL") +``` + +Require a clean posture from both sources for sensitive access: + +```go +("CrowdStrike" in account.risk_score && account.risk_score["CrowdStrike"] <= 30) + && !c1.app_user.v1.HasSecurityInsightWithSeverity(account, "Wiz Insights", "HIGH") + && !c1.app_user.v1.HasSecurityInsightWithSeverity(account, "Wiz Insights", "CRITICAL") +``` + +### CEL best practices for external insights + +- **Put cheap checks first.** Place simple attribute comparisons (like `subject.department`) before insight or risk-score lookups, which trigger lazy data loads. +- **Guard with existence checks.** Use `HasRiskScore` or `"CrowdStrike" in account.risk_score` before reading scores to avoid zero-default edge cases. Use `HasSecurityInsight` before iterating insights. +- **Use the correct source name.** The source name must exactly match the app display name in your tenant. +- **Prefer helper functions for severity checks.** `HasSecurityInsightWithSeverity` compares severity case-insensitively, but direct field comparisons like `i.severity == "CRITICAL"` are case-sensitive. + ## Supported external insights sources - Ingest Falcon identity risk scores into ConductorOne. + Ingest Falcon identity risk scores into C1. - Ingest Wiz identity risk scores into ConductorOne. + Ingest Wiz identity risk scores into C1.