Authoring Custom Rules
On Growth and above, your org can author its own UiPath risk-detection rules. A custom rule runs inside the same analyzer as the built-in rules — on every member's validation and every CI check — and its findings appear alongside the rest. Rules are JSON; the in-app editor builds that JSON for you, and this page is the reference behind it.
Anatomy of a rule
A rule matches an activity and, optionally, asserts conditions on that activity's properties. Here is a complete rule that flags an InvokeWorkflowFile activity which suppresses errors:
{
"id": "ORG-NO-CONTINUE",
"platform": "uipath",
"name": "No ContinueOnError on InvokeWorkflowFile",
"severity": "high",
"message": "InvokeWorkflowFile must not suppress errors with ContinueOnError.",
"enabled": true,
"match": {
"activity": "*.InvokeWorkflowFile",
"conditions": [
{ "property": "ContinueOnError", "equals": "True" }
]
}
}| Field | Rules | Description |
|---|---|---|
id | ^[A-Z][A-Z0-9-]{2,31}$, unique per org, may not start with FC- | Stable identifier. Surfaces on every finding this rule produces. |
platform | uipath only (v1) | The automation platform the rule targets. Other platforms are rejected for now. |
name | required | Short human label shown in the editor and rule list. |
severity | low · medium · high · critical | The severity stamped on every finding. Declared severity is final (no elevation). |
message | required, ≤ 500 chars | The finding text an engineer reads. Say what is wrong and what to do. |
enabled | boolean | Disabled rules are stored but never evaluated. |
match.activity | required, ≤ 200 chars | Activity type/name pattern, with * wildcards. See below. |
match.conditions | optional list | Property assertions, all AND-combined. Empty list matches every activity of that type. |
Matching activities
match.activity is matched against each activity's type/name in the workflow. A * stands for any run of characters, so *.InvokeWorkflowFile matches the InvokeWorkflowFile activity regardless of its namespace prefix, and *Click* matches any activity whose name contains Click. A pattern with no * must match the whole name. Matching is case-sensitive on the activity name and ignores the activity's DisplayName (use a condition on DisplayName if you need that).
Conditions
Each condition names a property (resolved against the activity's XAML attributes — ContinueOnError, TimeoutMS, WorkflowFileName, DisplayName, …) and exactly one match-type. All conditions on a rule must hold for the activity to be flagged.
| Match-type | Fires when |
|---|---|
{ "exists": true } | The attribute is present on the activity. |
{ "notExists": true } | The attribute is absent. |
{ "equals": "True" } | The attribute value equals the given string, case-insensitively. |
{ "contains": "http://" } | The attribute value contains the given substring. |
{ "regex": ".*Prod.*" } | The attribute value matches the given .NET regular expression. |
Composite rules (where)
For rules that a flat condition list can't express, replace match.conditions with a match.where expression tree. A node sets exactly one of the keys below; where and conditions are mutually exclusive on a rule.
| Node | Matches when |
|---|---|
{ "allOf": [ … ] } | Every child node matches. |
{ "anyOf": [ … ] } | At least one child node matches. |
{ "not": { … } } | The child node does not match. |
{ "attribute": { … } } | A condition (the same property + match-type shape) holds on the activity. |
{ "child": { "element": "*.Text", "where": … } } | A descendant element matching the name pattern exists (and satisfies its optional nested where). Use for properties expressed as child elements. |
{ "notInsideAncestor": "TryCatch" } | The activity has no ancestor activity matching the pattern. Combine with not to require the opposite. |
{ "siblingBefore": "*.LogMessage" } | An activity matching the pattern appears earlier under the same parent. siblingAfter checks the other direction. |
Structural checks (notInsideAncestor, siblingBefore, siblingAfter) always resolve against the activity matched by match.activity — even when nested inside a child block, where attribute checks instead apply to the matched child element.
Hardcoded path that isn't wrapped in a Try/Catch
{
"id": "ORG-INVOKE-UNGUARDED",
"platform": "uipath",
"name": "Hardcoded workflow path not wrapped in a Try/Catch",
"severity": "high",
"message": "InvokeWorkflowFile with an absolute path must run inside a Try/Catch.",
"enabled": true,
"match": {
"activity": "*.InvokeWorkflowFile",
"where": {
"allOf": [
{ "attribute": { "property": "WorkflowFileName", "regex": "^[A-Za-z]:\\\\" } },
{ "not": { "notInsideAncestor": "TryCatch" } }
]
}
}
}Author composite rules in the dashboard's Advanced (JSON) editor (rules with a where tree open there automatically), or ship them in a YAML pack imported via the app or rpagov rules import.
Limits & safety
| Limit | Value |
|---|---|
| Rules per org | 50 |
message length | ≤ 500 characters |
match.activity length | ≤ 200 characters |
where nesting depth | ≤ 5 levels |
where nodes per rule | ≤ 40 |
regex length | ≤ 500 characters, and it must compile |
| Regex evaluation | Bounded to 100 ms per match (ReDoS guard); a timeout counts as no match. |
| Score impact | None in v1 — custom findings surface in lists, counts, and exports but do not change the health score. |
More examples
Flag an environment-specific path (regex)
{
"id": "ORG-PROD-PATH",
"platform": "uipath",
"name": "Environment-specific path in InvokeWorkflowFile",
"severity": "medium",
"message": "Avoid hardcoded environment paths; resolve the workflow from a config asset.",
"enabled": true,
"match": {
"activity": "*.InvokeWorkflowFile",
"conditions": [
{ "property": "WorkflowFileName", "regex": ".*Prod.*" }
]
}
}Require an explicit timeout (notExists)
{
"id": "ORG-REQUIRE-TIMEOUT",
"platform": "uipath",
"name": "Click without an explicit timeout",
"severity": "low",
"message": "Set TimeoutMS on Click activities so a missing target cannot hang the run.",
"enabled": true,
"match": {
"activity": "*.Click",
"conditions": [
{ "property": "TimeoutMS", "notExists": true }
]
}
}Write your first rule
The fastest way in is the editor: in the dashboard go to Governance → Custom Rules, fill in the form, and test the draft against a real .xaml before you save — it shows exactly which activities the rule would flag. Saved rules take effect on the next validation for everyone in the org.
Browse the built-in rules in the rule library, or talk to us if you want a rule reviewed.