-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Aggregate rule number two, in which we aggregate entrypoint annotations, and fail if we don't find any. Fixes #154 Signed-off-by: Anders Eknert <anders@styra.com>
- Loading branch information
1 parent
52c57d3
commit f1ff4fb
Showing
8 changed files
with
209 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# METADATA | ||
# description: Missing entrypoint annotation | ||
package regal.rules.idiomatic["no-defined-entrypoint"] | ||
|
||
import future.keywords.contains | ||
import future.keywords.if | ||
import future.keywords.in | ||
|
||
import data.regal.ast | ||
import data.regal.result | ||
|
||
aggregate contains entry if { | ||
some annotation in input.annotations | ||
annotation.entrypoint == true | ||
|
||
entry := result.aggregate(rego.metadata.chain(), {"entrypoint": annotation.location}) | ||
} | ||
|
||
# METADATA | ||
# schemas: | ||
# - input: schema.regal.aggregate | ||
aggregate_report contains violation if { | ||
count(input.aggregate) == 0 | ||
|
||
violation := result.fail(rego.metadata.chain(), {}) | ||
} |
73 changes: 73 additions & 0 deletions
73
bundle/regal/rules/idiomatic/no_defined_entrypoint_test.rego
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package regal.rules.idiomatic["no-defined-entrypoint_test"] | ||
|
||
import future.keywords.if | ||
import future.keywords.in | ||
|
||
import data.regal.ast | ||
import data.regal.config | ||
|
||
import data.regal.rules.idiomatic["no-defined-entrypoint"] as rule | ||
|
||
test_aggregate_entrypoints if { | ||
module := regal.parse_module("policy.rego", ` | ||
# METADATA | ||
# entrypoint: true | ||
package p | ||
# METADATA | ||
# entrypoint: true | ||
allow := false`) | ||
|
||
aggregate := rule.aggregate with input as module | ||
aggregate == { | ||
{ | ||
"aggregate_data": {"entrypoint": {"col": 1, "file": "policy.rego", "row": 2, "text": "IyBNRVRBREFUQQ=="}}, | ||
"aggregate_source": {"file": "policy.rego", "package_path": ["p"]}, | ||
"rule": {"category": "idiomatic", "title": "no-defined-entrypoint"}, | ||
}, | ||
{ | ||
"aggregate_data": {"entrypoint": {"col": 1, "file": "policy.rego", "row": 6, "text": "IyBNRVRBREFUQQ=="}}, | ||
"aggregate_source": {"file": "policy.rego", "package_path": ["p"]}, | ||
"rule": {"category": "idiomatic", "title": "no-defined-entrypoint"}, | ||
}, | ||
} | ||
} | ||
|
||
test_fail_no_entrypoint_defined if { | ||
r := rule.aggregate_report with input as {"aggregate": set()} | ||
r == {{ | ||
"category": "idiomatic", | ||
"description": "Missing entrypoint annotation", | ||
"level": "error", | ||
"related_resources": [{ | ||
"description": "documentation", | ||
"ref": config.docs.resolve_url("$baseUrl/$category/no-defined-entrypoint", "idiomatic"), | ||
}], | ||
"title": "no-defined-entrypoint", | ||
}} | ||
} | ||
|
||
test_success_single_entrypoint_defined if { | ||
r := rule.aggregate_report with input as {"aggregate": [{ | ||
"aggregate_data": {"entrypoint": {"col": 1, "file": "policy.rego", "row": 2}}, | ||
"aggregate_source": {"file": "policy.rego", "package_path": ["p"]}, | ||
"rule": {"category": "idiomatic", "title": "no-defined-entrypoint"}, | ||
}]} | ||
r == set() | ||
} | ||
|
||
test_success_multiple_entrypoints_defined if { | ||
r := rule.aggregate_report with input as {"aggregate": [ | ||
{ | ||
"aggregate_data": {"entrypoint": {"col": 1, "file": "policy.rego", "row": 2}}, | ||
"aggregate_source": {"file": "policy.rego", "package_path": ["p"]}, | ||
"rule": {"category": "idiomatic", "title": "no-defined-entrypoint"}, | ||
}, | ||
{ | ||
"aggregate_data": {"entrypoint": {"col": 1, "file": "policy.rego", "row": 6}}, | ||
"aggregate_source": {"file": "policy.rego", "package_path": ["p"]}, | ||
"rule": {"category": "idiomatic", "title": "no-defined-entrypoint"}, | ||
}, | ||
]} | ||
r == set() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# no-defined-entrypoint | ||
|
||
**Summary**: Missing entrypoint annotation | ||
|
||
**Category**: Idiomatic | ||
|
||
**Type**: Aggregate - only runs when more than one file is provided for linting | ||
|
||
**Avoid** | ||
```rego | ||
package policy | ||
import future.keywords.if | ||
import future.keywords.in | ||
default allow := false | ||
# Nothing wrong with this rule, but an | ||
# entrypoint should be documented as such | ||
allow if user_is_admin | ||
allof if public_resource_read | ||
user_is_admin if { | ||
some role in input.user.roles | ||
role in data.permissions.admin_roles | ||
} | ||
public_resource_read if { | ||
input.request.method == "GET" | ||
input.request.path[0] == "public" | ||
} | ||
``` | ||
|
||
**Prefer** | ||
```rego | ||
package policy | ||
import future.keywords.if | ||
import future.keywords.in | ||
default allow := false | ||
# METADATA | ||
# description: Allow only admins, or reading public resources | ||
# entrypoint: true | ||
allow if user_is_admin | ||
allof if public_resource_read | ||
user_is_admin if { | ||
some role in input.user.roles | ||
role in data.permissions.admin_roles | ||
} | ||
public_resource_read if { | ||
input.request.method == "GET" | ||
input.request.path[0] == "public" | ||
} | ||
``` | ||
|
||
## Rationale | ||
|
||
Defining one or more entrypoints for your policies is a good practice to follow. An entrypoint is simply a package or | ||
rule that is meant to be queried for decisions from the outside. While it might seem obvious to the policy author which | ||
rules are meant to be queried, adding an extra line of two of metadata will help make it obvious to others. | ||
|
||
Marking a package or rule via an | ||
[entrypoint annotation attribute](https://www.openpolicyagent.org/docs/latest/policy-language/#entrypoint) not only | ||
provides good documentation for others, but also unlocks programmatic possibilities, like: | ||
|
||
1. Your policy library may be compiled to WebAssembly without extra entrypoint arguments | ||
1. Your policy library may be compiled to an | ||
[intermediate representation](https://blog.openpolicyagent.org/i-have-a-plan-exploring-the-opa-intermediate-representation-ir-format-7319cd94b37d) | ||
(IR) format without extra entrypoint arguments | ||
1. External applications may present your entrypoints as part of rendered documentation | ||
1. External applications may use your entrypoints to know what to evaluate | ||
1. External applications — like Regal — may use this information to determine what other rules are used or not | ||
|
||
## Configuration Options | ||
|
||
This linter rule provides the following configuration options: | ||
|
||
```yaml | ||
rules: | ||
idiomatic: | ||
no-defined-entrypoint: | ||
# one of "error", "warning", "ignore" | ||
level: error | ||
``` | ||
|
||
## Related Resources | ||
|
||
- OPA Docs [Metadata](https://www.openpolicyagent.org/docs/latest/policy-language/#metadata) | ||
- OPA Docs [Entrypoint](https://www.openpolicyagent.org/docs/latest/policy-language/#entrypoint) | ||
|
||
## Community | ||
|
||
If you think you've found a problem with this rule or its documentation, would like to suggest improvements, new rules, | ||
or just talk about Regal in general, please join us in the `#regal` channel in the Styra Community | ||
[Slack](https://communityinviter.com/apps/styracommunity/signup)! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
package mypolicy1.public | ||
|
||
# METADATA | ||
# entrypoint: true | ||
my_policy_1 := true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
package mypolicy1.public | ||
|
||
# METADATA | ||
# entrypoint: true | ||
my_policy_1 := true |