Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ sidebar_label: "ADR-0002"
|----------------|----------|
| **Status**: | ACCEPTED |
| **Date**: | 2020-05-20 |
| **Author(s)**: | Jannik Hollenbach [jannick.hollenbach@iteratec.com](mailto:jannick.hollenbach@iteratec.com), Jorge Estigarribia [jorge.estigarribia@iteratec.com](mailto:jorge.estigarribia@iteratec.com), Robert Seedorff [Robert.Seedorff@iteratec.com](mailto:Robert.Seedorff@iteratec.com), Sven Strittmatter [sven.strittmatter@iteratec.com](mailto:Sven.Strittmatter@iteratec.com) |
| **Author(s)**: | Jannik Hollenbach [jannik.hollenbach@iteratec.com](mailto:jannik.hollenbach@iteratec.com), Jorge Estigarribia [jorge.estigarribia@iteratec.com](mailto:jorge.estigarribia@iteratec.com), Robert Seedorff [Robert.Seedorff@iteratec.com](mailto:Robert.Seedorff@iteratec.com), Sven Strittmatter [sven.strittmatter@iteratec.com](mailto:Sven.Strittmatter@iteratec.com) |

## Context

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ sidebar_label: "ADR-0003"
|----------------|----------|
| **Status**: | ACCEPTED |
| **Date**: | 2020-05-20 |
| **Author(s)**: | Jannik Hollenbach [jannick.hollenbach@iteratec.com](mailto:jannick.hollenbach@iteratec.com), Robert Seedorff [Robert.Seedorff@iteratec.com](mailto:Robert.Seedorff@iteratec.com), Sven Strittmatter [sven.strittmatter@iteratec.com](mailto:Sven.Strittmatter@iteratec.com) |
| **Author(s)**: | Jannik Hollenbach [jannik.hollenbach@iteratec.com](mailto:jannik.hollenbach@iteratec.com), Robert Seedorff [Robert.Seedorff@iteratec.com](mailto:Robert.Seedorff@iteratec.com), Sven Strittmatter [sven.strittmatter@iteratec.com](mailto:Sven.Strittmatter@iteratec.com) |

## Context

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ sidebar_label: "ADR-0009"
|----------------|----------|
| **Status**: | ACCEPTED |
| **Date**: | 2021-10-07 |
| **Author(s)**: | Max Maass [max.maass@iteratec.com](mailto:max.maass@iteratec.com), Jannik Hollenbach [jannick.hollenbach@iteratec.com](mailto:jannick.hollenbach@iteratec.com) |
| **Author(s)**: | Max Maass [max.maass@iteratec.com](mailto:max.maass@iteratec.com), Jannik Hollenbach [jannik.hollenbach@iteratec.com](mailto:jannik.hollenbach@iteratec.com) |

## Context

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sidebar_label: "ADR-0012"
| -------------- | -------------------------------------------------------------------------------------- |
| **Status**: | OPEN |
| **Date**: | 2022-06-17 |
| **Author(s)**: | Jannik Hollenbach [jannick.hollenbach@iteratec.com](mailto:jannick.hollenbach@iteratec.com), Max Maass [max.maass@iteratec.com](mailto:max.maass@iteratec.com) |
| **Author(s)**: | Jannik Hollenbach [jannik.hollenbach@iteratec.com](mailto:jannik.hollenbach@iteratec.com), Max Maass [max.maass@iteratec.com](mailto:max.maass@iteratec.com) |

## Context

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sidebar_label: "ADR-0015"
| -------------- | ------------------------------------------------------------------------------------------------------ |
| **Status**: | ACCEPTED |
| **Date**: | 2022-09-13 |
| **Author(s)**: | Jannik Hollenbach [jannick.hollenbach@iteratec.com](mailto:jannick.hollenbach@iteratec.com), Sven Strittmatter [sven.strittmatter@iteratec.com](mailto:Sven.Strittmatter@iteratec.com) |
| **Author(s)**: | Jannik Hollenbach [jannik.hollenbach@iteratec.com](mailto:jannik.hollenbach@iteratec.com), Sven Strittmatter [sven.strittmatter@iteratec.com](mailto:Sven.Strittmatter@iteratec.com) |

:::info
This ADR should have been written prior to implementation. But we started documenting ADR later. This ADR has therefore been written retrospectively to record the decision made at that time.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sidebar_label: "ADR-0016"
| -------------- | ------------------------------------------------------------------------------------------------------ |
| **Status**: | ACCEPTED |
| **Date**: | 2022-09-13 |
| **Author(s)**: | Jannik Hollenbach [jannick.hollenbach@iteratec.com](mailto:jannick.hollenbach@iteratec.com), Sven Strittmatter [sven.strittmatter@iteratec.com](mailto:Sven.Strittmatter@iteratec.com) |
| **Author(s)**: | Jannik Hollenbach [jannik.hollenbach@iteratec.com](mailto:jannik.hollenbach@iteratec.com), Sven Strittmatter [sven.strittmatter@iteratec.com](mailto:Sven.Strittmatter@iteratec.com) |

:::info
This ADR should have been written prior to implementation. But we started documenting ADR later. This ADR has therefore been written retrospectively to record the decision made at that time.
Expand Down
211 changes: 211 additions & 0 deletions documentation/docs/architecture/09_architecture_decisions/adr_0020.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
---
# SPDX-FileCopyrightText: the secureCodeBox authors
#
# SPDX-License-Identifier: Apache-2.0

title: "ADR-0020: Adopting Common Expression Language (CEL) for CascadingRule Matching"
sidebar_label: "ADR-0020"
---
# ADR-0020: Adopting Common Expression Language (CEL) for CascadingRule Matching

| <!-- --> | <!-- --> |
|----------------|----------------------------------------------------------------------------------------------|
| **Status**: | PROPOSED |
| **Date**: | 2025-10-14 |
| **Author(s)**: | Jannik Hollenbach [jannik.hollenbach@iteratec.com](mailto:jannik.hollenbach@iteratec.com) |

## Context

CascadingRules in secureCodeBox currently use a custom `matches` object syntax to define which findings should trigger subsequent scans. The current implementation uses a declarative YAML structure with `anyOf` rules that perform partial deep comparison against finding fields:

```yaml
spec:
matches:
anyOf:
- category: "Open Port"
attributes:
port: 22
state: open
- category: "Open Port"
attributes:
service: "ssh"
state: open
```

While this approach works well for simple matching scenarios, it has several limitations:

1. **Limited Expressiveness**: The current syntax only supports exact matching and partial deep comparison. Complex conditions like range checks, regex patterns, logical combinations beyond `anyOf`, or computed values are not possible without extending the custom syntax.
2. **Maintenance Burden**: Every new matching requirement necessitates extending the custom matcher implementation. This creates ongoing maintenance overhead and increases the complexity of the codebase.
3. **Lack of Flexibility**: Common use cases like checking if a port is within a range (e.g., `port >= 8000 && port <= 9000`), matching against multiple patterns, or combining conditions with complex boolean logic require workarounds or are simply not possible.

[Common Expression Language (CEL)](https://github.com/google/cel-spec) is a non-Turing complete expression language designed for evaluating expressions in a safe, fast, and portable manner. It is already widely adopted in the Kubernetes ecosystem, particularly in:

- Kubernetes ValidatingAdmissionPolicy (since v1.26)
- Kubernetes Custom Resource Definitions (CRD validation rules)
- Istio authorization policies
- Various other cloud-native projects

CEL provides a familiar C-like syntax and is specifically designed for configuration and policy evaluation use cases, making it an ideal fit for CascadingRule matching logic.

## Decision

We propose migrating the CascadingRule `matches` specification from the current custom object syntax to use Common Expression Language (CEL) expressions.

### Proposed Syntax

Instead of the current `matches.anyOf` structure, users would write CEL expressions that evaluate to a boolean:

```yaml
spec:
matches:
expression: |
(finding.category == "Open Port" && finding.attributes.port == 22 && finding.attributes.state == "open") ||
(finding.category == "Open Port" && finding.attributes.service == "ssh" && finding.attributes.state == "open")
```

Or more concisely:

```yaml
spec:
matches:
expression: |
finding.category == "Open Port" &&
finding.attributes.state == "open" &&
(finding.attributes.port == 22 || finding.attributes.service == "ssh")
```

### Advanced Use Cases Enabled by CEL

CEL would enable powerful matching scenarios that are currently impossible:

**Range Checks:**
```yaml
expression: |
finding.category == "Open Port" &&
finding.attributes.port >= 8000 &&
finding.attributes.port <= 9000
```

**Regex Matching:**
```yaml
expression: |
finding.category == "Subdomain" &&
finding.attributes.hostname.matches("^.*\\.example\\.com$")
```

**Complex Boolean Logic:**
```yaml
expression: |
(finding.severity in ["HIGH", "CRITICAL"] && finding.category == "Vulnerability") ||
(finding.category == "Open Port" && finding.attributes.port in [22, 23, 3389])
```

**Computed Values:**
```yaml
expression: |
finding.category == "Open Port" &&
has(finding.attributes.service) &&
finding.attributes.service.startsWith("http")
```

### Migration Strategy

To ensure backward compatibility and smooth migration:

1. **Dual Support Period**: Support both the legacy `matches.anyOf` syntax and the new `matches.expression` syntax simultaneously for at least two major versions.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if accidentally both is configured? Precedence of CEL + warning?


2. **Automatic Translation (Preferred Approach)**: Implement automatic runtime translation of `matches.anyOf` to CEL expressions:
- When a CascadingRule contains only `matches.anyOf`, automatically translate it to an equivalent CEL expression at runtime
- Log an informational message indicating the automatic translation occurred
- This approach eliminates the need for complex conflict resolution logic
- Users can gradually migrate at their own pace without breaking changes
- The translation logic can be removed in a future major version when `anyOf` support is dropped

**If automatic translation is implemented, the conflict resolution below becomes unnecessary.**

3. **Conflict Resolution (Alternative if no automatic translation)**: When both `matches.anyOf` and `matches.expression` are specified in the same CascadingRule:
- Generally automatic translation would be prefered as it eliminates manual work and potential errors, but if that turns out to be hard to achieve, manual translation might be the only option.
- **CEL takes precedence**: The `matches.expression` will be evaluated and `matches.anyOf` will be ignored
- **Emit a warning**: Log a warning message and add a Kubernetes event to the CascadingRule resource indicating the conflict
- **Add status condition**: Update the CascadingRule status with a condition indicating that both matchers were specified and CEL was used

Example warning message:
```
Warning: CascadingRule 'nmap-hostscan' specifies both 'matches.anyOf' and 'matches.expression'.
Using CEL expression and ignoring anyOf matcher. Please remove the deprecated 'matches.anyOf' field.
```

4. **Translation Documentation**: Provide documentation to help users manually translate existing `anyOf` rules to CEL expressions to better help users understanding of the new syntax.

5. (consider) **Validation**: Implement comprehensive validation of CEL expressions at CRD admission time to catch syntax errors early. We have avoided validating webhooks so far, as they have a overhead in terms of cert management, but might be worthwhile for this issue.

6. **Documentation**: Create extensive documentation with examples showing common patterns and migration guides.

7. **Proposed Deprecation Path**:
- Version 5.x: Introduce CEL support with automatic translation of `anyOf` (if implemented) or dual support, mark `anyOf` as deprecated with informational/warning messages
- Version 6.0.0: Remove support for `anyOf` syntax and automatic translation logic

## Consequences

### Positive Consequences

1. **Increased Flexibility**: Users can express arbitrarily complex matching logic without waiting for custom syntax extensions.
2. **Reduced Maintenance**: The secureCodeBox team no longer needs to maintain and extend custom matching logic. CEL is maintained by Google and the broader community.
3. **Industry Standard**: CEL is becoming the de facto standard for policy expressions in Kubernetes, making it familiar to many users.
4. **Better Tooling**: CEL has existing tooling, documentation, and community support that users can leverage.
5. **Type Safety**: CEL provides compile-time type checking, catching errors before runtime.
6. **Security**: CEL is non-Turing complete and designed to be safe for user-provided expressions, preventing infinite loops or resource exhaustion.

### Negative Consequences

1. **Breaking Change**: Eventually removing the `anyOf` syntax will require users to migrate their existing CascadingRules.
2. **Learning Curve**: Users unfamiliar with CEL will need to learn a new expression syntax, though it's relatively simple and well-documented.
3. **Migration Effort**: Existing CascadingRules will need to be updated, requiring effort from users and clear migration documentation.
4. **Increased Complexity**: The cascading hook codebase will temporarily be more complex during the dual-support period.
5. **Dependency Addition**: Adding the cel library increases the dependency footprint of the operator.
6. **Error Messages**: CEL error messages may be less intuitive than custom validation errors, requiring careful wrapping and contextualization.
7. **Potential Confusion**: During the dual-support period, users might accidentally specify both matchers, though the clear precedence rule and warnings mitigate this risk.

### Mitigation Strategies

- **If automatic translation is implemented**: The migration becomes seamless with minimal user impact
- Provide comprehensive migration guides with side-by-side examples
- Create a validation tool or script to help users test their CEL expressions
- Maintain the dual-support period for sufficient time to allow gradual migration
- Offer community support and examples for common migration scenarios
- If no automatic translation: Implement clear conflict detection with actionable warning messages
- Add linting/validation in CI/CD pipelines to detect deprecated usage early

## Alternatives Considered

### 1. Extend the Current Custom Syntax

We could continue extending the `matches` object with new fields and operators (e.g., `allOf`, `noneOf`, etc.).

**Rejected because**: This would perpetuate the maintenance burden and still wouldn't provide the full flexibility of a proper expression language. Each new requirement would require code changes and releases.

### 2. Use JSONPath or JMESPath

These are query languages designed for JSON data extraction and filtering.

**Rejected because**: While powerful for data extraction, they are less intuitive for boolean logic and condition evaluation. CEL is specifically designed for policy evaluation use cases.

### 3. Use JavaScript or Lua

Embed a scripting language for maximum flexibility.

**Rejected because**: Full scripting languages are Turing complete and pose security risks when evaluating user-provided code. They also have higher performance overhead and complexity. CEL's non-Turing complete nature makes it safer and more appropriate for this use case.

### 4. Use Rego (Open Policy Agent)

Rego is the policy language used by Open Policy Agent.

**Rejected because**: While Rego is powerful, it has a steeper learning curve and is less widely adopted in the Kubernetes ecosystem compared to CEL. CEL's integration with Kubernetes CRDs and admission policies makes it a more natural fit.

## References

- [CEL Specification](https://github.com/google/cel-spec)
- [cel-go Implementation](https://github.com/google/cel-go)
- [cel-js Implementation](https://github.com/marcbachmann/cel-js)
- [Kubernetes CEL Validation](https://kubernetes.io/docs/reference/using-api/cel/)
- [Current CascadingRule Documentation](https://www.securecodebox.io/docs/api/crds/cascading-rule)
Loading