Linting policies

This topic describes how to lint (validate) your Identity and Access Management (IAM) policies.

Before you begin

Understanding linting for IAM policies

In the context of IAM policies, linting is a method of examining a new or existing policy and checking it for specific issues. These issues include the following range of possibilities:

  • Suggestions
  • Warnings
  • Information that can help improve the intent of the policy, such as better syntax and semantics
  • Syntax or semantic errors that will cause setIamPolicy operations to fail

Linting a policy can be used as the debugging tool to provide a detailed explanation for any failed setIamPolicy() operations. It can also be used as the helper tool to facilitate authoring an IAM policy to achieve the intended result.

Linting a condition

Condition expressions can often become complex, especially in scenarios that require multiple clauses and logic operators to appropriately restrict access. If a condition expression contains invalid logic, or if the syntax violates the restrictions of a condition expression, the setIamPolicy() operation will fail. In addition, while a condition may pass all strict syntax and semantics requirements for policy update, it may still contain constructs that may lead to unexpected access results during policy enforcement. A few examples of such cases include the following:

  • Use of unrecommended functions
  • Use of legacy resource types or legacy service names
  • Ineffective conditions, such as an inapplicable date or time range

Linting a condition will conduct a thorough inspection and will report both the failures that may happen in policy update as well as other questionable constructs.

Before attempting to set a new conditional role binding, you are encouraged to lint the expression first. This section shows you how to lint a condition expression using the Cloud Console, gcloud command-line tool, or the REST API.

To lint a condition expression:

Console

  1. In the Cloud Console, go to the IAM page.

    Go to the IAM page

  2. Click Select a project, choose a project, and click Open.

  3. From the list of members, locate the desired member and click the button.

  4. From the Edit permissions panel, locate the desired role that you want to lint. Then under Condition, click the name of the condition.

  5. In the Edit condition panel, the Condition Editor provides the capability of linting the expression. The Condition Builder currently has limited linting functionality.

  6. In the Condition Editor, manually add or edit a condition expression.

  7. Validate the CEL syntax by clicking Run Linter above the text box on the top-right. If the syntax contains errors, an icon will appear next to each line of the expression. If you hover your pointer over the icon, the error message for that portion of the expression will appear, and provide some actionable feedback for debugging.

    We recommend that you fix the errors before saving the condition. If the conditions is syntactically correct but the lint operation finds questionable constructs, a warning icon () will appear. If you hover your pointer over the icon, the warning message for that portion of the expression will appear, and provide some actionable feedback for debugging. While it's strongly recommended, it's not a strict requirement to fix the warnings before saving the condition.

  8. Make any necessary changes to the condition expression. After Run Linter is triggered, it will continue to lint in the background to facilitate the editing of the expression.

  9. After the epxression has been successfully linted, click Save to apply the condition.

  10. Once the Edit condition panel is closed, click Save again from the Edit permissions panel to update your IAM policy.

gcloud

Execute the gcloud alpha iam policies lint-condition command to lint a given condition expression. To execute this command, you can either create a text file that contains the condition, or specify flags for the condition's title, description, and expression.

In the example below, we will use a text file that contains the condition:

condition.json

"condition": {
    "title": "1_less_than_2",
    "description": "",
    "expression": "1 <"
}

Command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

The output from the command contains the following:

lintResults:
- debugMessage: |-
    ERROR: Parse expression:1:3: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
      | 1 >
      | ...^
  fieldName: condition.expression
  level: CONDITION
  locationOffset: 3
  severity: ERROR
  validationUnitName: LintValidationUnits/ConditionCompileCheck
...

Each of the lint results contain a debugMessage that can be used to help locate the problem with the condition expression. If the condition failed to compile, you may see many different validationUnitName types with the following debugMessage text:

The validation unit is skipped due to absence of a required object: CheckedExpr

Make any required changes so that the expression will successfully compile, and then attempt to lint the condition again.

REST

The iamPolicies.lintPolicy method lints, or validates, a condition expression in an IAM policy.

Before using any of the request data below, make the following replacements:

  • condition: An Expr object representing the condition to lint. For example:

    "title": "1_less_than_2",
    "description": "",
    "expression": "1 <"
    
    To learn about the format of an Expr object, see the Expr schema reference.

HTTP method and URL:

POST https://iam.googleapis.com/v1/iamPolicies:lintPolicy

Request JSON body:

{
  "condition": {
    condition
  }
}

To send your request, expand one of these options:

The response body contains one or more LintResult objects, such as the following:

{
  "lint_results": {
    "level": "CONDITION",
    "validation_unit_name": "LintValidationUnits/ConditionCompileCheck",
    "severity": "ERROR",
    "field_name": "condition.expression",
    "location_offset": "2",
    "debug_message": "ERROR: Parse expression:1:2: mismatched input \'<EOF>\' expecting {\'[\', \'{\', \'(\', \'.\', \'-\', \'!\', \'true\', \'false\', \'null\', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}\n  | 1<\n  | ..^"
  },
  "lint_results": {
    "level": "CONDITION",
    "validation_unit_name": "LintValidationUnits/ConditionComplexityCheck",
    "severity": "NOTICE",
    "field_name": "condition.expression",
    "debug_message": "The validation unit is skipped due to absence of a required object: CheckedExpr"
  }
}

Each of the lint results contain a debug_message that can be used to help locate the problem with the condition expression. If the condition failed to compile, you may see many different validation_unit_name types with the following debugMessage text:

The validation unit is skipped due to absence of a required object: CheckedExpr

Make any required changes so that the expression will successfully compile, and then attempt to lint the condition expression again.

Supported validation units

As described in the section above, a validation unit is an individual lint type that evaluates the expression for syntactic issues. The table below summarizes supported validation units, each with intended linting level, linting result severity, and a brief description.

Validation unit Lint Level Severity Description
ConditionCompileCheck CONDITION ERROR The condition expression contains a compilation error as a result of invalid CEL syntax.
ConditionComplexityCheck CONDITION ERROR The condition expression contains more than the maximum of 12 logic operators.
DateTimeCheck CONDITION WARNING The condition expression specifies a timestamp comparison is effectively true or false, due to an invalid timestamp format (other than ISO 8601), or the expiration timestamp is in the past, or the start timestamp is in the past.
DateTimeRangeCheck CONDITION WARNING Value out of range for the intended advanced timestamp function and the comparison expression. See here for the valid value range for advanced timestamp functions.
EffectiveTimeRangeCheck CONDITION WARNING In a more complex usage of timestamp functions and comparison, the expression results in an empty effective time range, and is therefore effectively false. Alternatively, the time range covers a full range, and is therefore effectively true.
ResourceServiceLiteralCheck CONDITION WARNING The specified resource.service value is not supported. The expression using such string literal for equality comparison is effectively false. See here for supported values.
ResourceTypeLiteralCheck CONDITION WARNING The specified resource.type value is not supported. The expression using such string literal for equality comparison is effectively false. See here for supported values.
RestrictedAttributesCheck CONDITION WARNING The expression uses an attribute that is currently restricted or not supported. Setting the condition expression may not succeed. See here for the list of attributes and the release stages.

Linting examples

This section provides example usage of linting policy for each validation unit. For each validation unit, the example problematic input is provided. Each example demonstrates linting by using the gcloud command-line tool

ConditionCompileCheck

Example condition expression:

{
    "condition": {
      "title": "Condition not compiling",
      "description": "",
      "expression":
        "true=false"
    }
}

Run command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

Lint result:

lintResults:
- debugMessage: |-
    ERROR: Parse expression:1:4: token recognition error at: '=f'
      | true=false
      | ....^
  fieldName: condition.expression
  level: CONDITION
  locationOffset: 4
  severity: ERROR
  validationUnitName: LintValidationUnits/ConditionCompileCheck

ConditionComplexityCheck

Example condition expression:

{
    "condition": {
      "title": "Condition not compiling",
      "description": "",
      "expression":
        "1<2 || 2<3 || 3<4 || 4<5 || 5<6 || 6<7 || 7<8 || 8<9 || 9<10 || 10<11
        || 11<12 || 12<13 || 13<14 || 14<15"
    }
}

Run command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

Lint result:

lintResults:
- debugMessage: Logical operators count must not be more than 12
  fieldName: condition.expression
  level: CONDITION
  severity: ERROR
  validationUnitName: LintValidationUnits/ConditionComplexityCheck

DateTimeCheck

Example condition expression:

{
    "condition": {
      "title": "Condition not compiling",
      "description": "",
      "expression": "request.time < timestamp('2000-01-01T00:00:00Z')"
    }
}

Run command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

Lint result:

lintResults:
- debugMessage: Ineffective date time value 2000-01-01T00:00:00+00:00 parsed from
    "2000-01-01T00:00:00Z"; condition is effectively False. Time expired already.
  fieldName: condition.expression
  level: CONDITION
  locationOffset: 25
  severity: WARNING
  validationUnitName: LintValidationUnits/DateTimeCheck

DateTimeRangeCheck

Example condition expression:

{
    "condition": {
      "title": "Time function out of range",
      "description": "",
      "expression":
        "request.time.getMonth() > 13"
    }
}

Run command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

Lint result:

lintResults:
- debugMessage: The value being compared to the specified timestamp function (getMonth)
    must be in range [0, 11].
  fieldName: condition.expression
  level: CONDITION
  locationOffset: 26
  severity: WARNING
  validationUnitName: LintValidationUnits/DateTimeRangeCheck

EffectiveTimeRangeCheck

Example condition expression:

{
    "condition": {
      "title": "Empty time range",
      "description": "",
      "expression":
        "request.time.getMonth() > 5 && request.time.getMonth() < 4"
    }
}

Run command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

Lint result:

lintResults:
- debugMessage: The aggregate of time functions [getMonth] results in empty ranges.
  fieldName: condition.expression
  level: CONDITION
  severity: WARNING
  validationUnitName: LintValidationUnits/EffectiveTimeRangeCheck

ResourceServiceLiteralCheck

Example condition expression:

{
    "condition": {
      "title": "Condition with unsupported resource service string",
      "description": "",
      "expression":
        "resource.service == 'resourcemanager'"
    }
}

Run command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

Lint result:

lintResults:
- debugMessage: 'resource.service : resourcemanager is not supported. Using this
    value in condition may lead to unintended consequences. Check user guide at
    https://cloud.google.com/iam/docs/conditions-resource-attributes#resource_service_values
    for supported values for resource.service.'
  fieldName: condition.expression
  level: CONDITION
  locationOffset: 20
  severity: WARNING
  validationUnitName: LintValidationUnits/ResourceServiceLiteralCheck

ResourceTypeLiteralCheck

Example condition expression:

{
    "condition": {
      "title": "Condition with legacy resource type",
      "description": "",
      "expression":
        "resource.type == 'resourcemanager.projects'"
    }
}

Run command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

Lint result:

lintResults:
- debugMessage: 'resource.type : resourcemanager.projects is not supported.
    Using this value in condition may lead to unintended consequences. Check
    user guide at https://cloud.google.com/iam/docs/conditions-resource-attributes#resource_type_values
    for supported values for resource.type.'
  fieldName: condition.expression
  level: CONDITION
  locationOffset: 17
  severity: WARNING
  validationUnitName: LintValidationUnits/ResourceTypeLiteralCheck

RestrictedAttributesCheck

Example condition expression:

{
    "condition": {
      "title": "Condition with restricted attribute",
      "description": "",
      "expression":
        "'accessPolicies/123/accesslevels/TRUSTED' in request.auth.access_levels"
    }
}

Run command:

gcloud alpha iam policies lint-condition --condition-from-file=`condition.json`

Lint result:

lintResults:
- debugMessage: Condition attribute `request.auth.access_levels` is restricted
  or unsupported. Please check https://cloud.google.com/iam/docs/conditions-overview
    for the full list of supported attributes
  fieldName: condition.expression
  level: CONDITION
  locationOffset: 57
  severity: WARNING
  validationUnitName: LintValidationUnits/RestrictedAttributesCheck