-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7050528
Showing
25 changed files
with
2,946 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: main | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
pull_request: | ||
branches: | ||
- master | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-18.04 | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-go@v3 | ||
with: | ||
go-version: 1.18 | ||
- name: Run Tests | ||
run: make test | ||
- uses: codecov/codecov-action@v2 | ||
with: | ||
token: ${{ secrets.CODECOV_TOKEN }} |
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,16 @@ | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
policies-service | ||
__debug_bin | ||
*.test | ||
*.out | ||
bin/ | ||
coverage.txt | ||
coverage.html | ||
input.yaml | ||
rule.yaml | ||
rule.json | ||
.vscode |
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,6 @@ | ||
build: | ||
go build | ||
|
||
test: | ||
go test -v -race -cover -covermode=atomic -coverprofile=coverage.txt -coverpkg=github.com/ahsayde/yapl/yapl,github.com/ahsayde/yapl/internal/operator,github.com/ahsayde/yapl/internal/parser,github.com/ahsayde/yapl/internal/renderer ./... | ||
go tool cover -html=coverage.txt -o coverage.html |
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,260 @@ | ||
|
||
# YAPL | ||
*YAML as Policy Language* | ||
|
||
[![codecov](https://codecov.io/gh/ahsayde/yapl/branch/master/graph/badge.svg?token=N5CJSZBHNF)](https://codecov.io/gh/ahsayde/yapl) | ||
![example workflow](https://github.com/ahsayde/yapl/actions/workflows/main.yml/badge.svg) | ||
|
||
|
||
- [Syntax](#syntax) | ||
- [Usage](#usage) | ||
- [Contexts](#contexts) | ||
- [Operators](#operators) | ||
|
||
|
||
## Syntax | ||
|
||
|
||
### Selecting Resources | ||
|
||
The `match` and `exclude` are optional declarations used to filter resources which will be validated by the rules. | ||
|
||
They have the same syntax, and can be used together to include and exclude resources. | ||
|
||
#### Examples | ||
|
||
For example this code will only validate resources when `request.method` is equal `GET` | ||
|
||
```yaml | ||
match: | ||
field: request.method | ||
operator: equal | ||
value: GET | ||
``` | ||
this code will validate all resources except when the `request.method` is equal `GET` | ||
|
||
```yaml | ||
exclude: | ||
field: request.method | ||
operator: equal | ||
value: GET | ||
``` | ||
|
||
this code will validate all resources when `request.method` equals `GET` and `request.endpoint` not equals `/api` | ||
|
||
```yaml | ||
match: | ||
field: request.method | ||
operator: equal | ||
value: GET | ||
exclude: | ||
field: request.endpoint | ||
operator: equal | ||
value: /api | ||
``` | ||
|
||
the previous example can be done using only `match` statement as follow: | ||
|
||
```yaml | ||
match: | ||
and: | ||
- field: request.method | ||
operator: equal | ||
value: GET | ||
- not: | ||
field: request.endpoint | ||
operator: equal | ||
value: /api | ||
``` | ||
|
||
### Validating Resources | ||
|
||
Policies can have multiple rules. Each rule has its own condition and result object. | ||
|
||
rule's condition doesn't support nested conditions like `match` and `exclude`, It's only one level | ||
|
||
```yaml | ||
rules: | ||
- condition: | ||
field: metadata.name | ||
equal: hasPrefix | ||
value: app | ||
result: container name must starts with app | ||
``` | ||
|
||
the `result` field could be an object, see this example | ||
|
||
```yaml | ||
rules: | ||
- condition: | ||
field: metadata.name | ||
equal: hasPrefix | ||
value: app | ||
result: | ||
msg: container name must starts with app | ||
``` | ||
|
||
#### Conditional Rule | ||
|
||
You can add conditional rule by adding field `when` which implements the `condition` object | ||
|
||
In this example, instead for writing two policies for each container type. Ypu can add two conditional rules | ||
|
||
```yaml | ||
rules: | ||
- when: | ||
field: image | ||
equal: equal | ||
value: my-app-image | ||
condition: | ||
field: metadata.name | ||
equal: hasPrefix | ||
value: app | ||
result: app container's name must starts with app | ||
- when: | ||
field: image | ||
equal: equal | ||
value: my-db-image | ||
condition: | ||
field: metadata.name | ||
equal: hasPrefix | ||
value: db | ||
result: db container's name must starts with db | ||
``` | ||
|
||
### Context | ||
|
||
Contexts are a way to access information about current rule | ||
|
||
### Available Contexts | ||
|
||
| Name | Type | Description | Availability | | ||
|--------------|-----------------|--------------------------------------------------|---------------------------------| | ||
| [`Input`](#input-context) | `object` | The input to be checked | all fields | | ||
| [`Params`](#params-context) | `object` | Parameters passed during evaulation | all fields | | ||
| [`Env`](#env-context) | `object` | exported environment variable | all fields | | ||
| [`Cond`](#condition-context) | `object` | Current condition information, | only on `rules.result` field | | ||
|
||
|
||
#### `Input` Context | ||
|
||
Input contex allow you to access the resource object | ||
|
||
```yaml | ||
rules: | ||
- condition: | ||
field: user.role | ||
operator: equal | ||
value: admin | ||
result: user ${ .Input.user.name } doesn't has access | ||
``` | ||
|
||
#### `Params` Context | ||
|
||
You can access parameters passed during the evaluation of input using `${ .Params.<variable name> }` expression. For example | ||
|
||
```yaml | ||
rules: | ||
- condition: | ||
field: request.body | ||
operator: maxLength | ||
value: ${ .Params.max_body_size } | ||
result: request body must not exceed ${ .Params.max_body_size } | ||
``` | ||
|
||
#### `Env` context | ||
|
||
You can access any environment variable value by using expression `${ .Env.<variable name> }` | ||
|
||
```yaml | ||
rules: | ||
- condition: | ||
field: request.body | ||
operator: maxLength | ||
value: ${ .Env.MAX_BODY_SIZE } | ||
result: request body must not exceed ${ .Env.MAX_BODY_SIZE } | ||
``` | ||
|
||
#### `Cond` context | ||
|
||
`Cond` context allow you to access all the information of the current field. | ||
|
||
This context is only availabe on field `rules.result`. | ||
|
||
|
||
| Key | Type | Description | | ||
|----------------------------------|-------------------|-------------------------------------------------------| | ||
| `Cond.Field.Value` | `any` | The value of the field | | ||
| `Cond.Field.Index` | `integer` | The index of the field if field's parent is an array | | ||
| `Cond.Field.Parent` | `[field object]` | The parent of the field | | ||
| `Cond.Operator` | `string` | Condition's operator | | ||
| `Cond.Value` | `string` | Condition's value | | ||
|
||
Example | ||
|
||
```yaml | ||
rules: | ||
- condition: | ||
field: metadata.labels.app | ||
operator: hasPrefix | ||
value: app | ||
result: | ||
msg: resource app label must starts with ${ .Cond.Value } but found ${ .Cond.Field.Value } | ||
key: ${ .Field.Path } | ||
``` | ||
|
||
|
||
|
||
## Usage | ||
|
||
```go | ||
input := map[string]interface{}{ | ||
"users": []interface{}{ | ||
map[string]interface{}{ | ||
"name": "bob", | ||
"role": "member", | ||
}, | ||
}, | ||
} | ||
params := map[string]interface{}{ | ||
"role": "admin", | ||
} | ||
raw, err := ioutil.ReadFile("policy.yaml") | ||
if err != nil { | ||
panic(err) | ||
} | ||
policy, err := yapl.Parse(raw) | ||
if err != nil { | ||
panic(err) | ||
} | ||
result, err := policy.Eval(input, params) | ||
if err != nil { | ||
panic(err) | ||
} | ||
``` | ||
|
||
|
||
## Operators | ||
|
||
| Operator | Alias | Description | Field Value | Operator Value | | ||
|-------------|----------|---------------------------------------------------------------------------|-------------|----------------| | ||
| `exists` | | checks whether field is exist or not | `any` | | | ||
| `equal` | `eq` | asserts field's value equal provided value | `any` | `any` | | ||
| `hasPrefix` | | checks whether field's value begins with prefix | `string` | `string` | | ||
| `hasSuffix` | | checks whether field's value ends with suffix | `string` | `string` | | ||
| `regex` | | checks whether field's value matches the provided regex | `string` | `string` | | ||
| `minValue` | `min` | checks whether field's value is greater than or equals provided value | `number` | `number` | | ||
| `maxValue` | `max` | checks whether field's value is less than or equals the provided value | `number` | `number` | | ||
| `in` | | checks whether field's value in the provided value | `any` | `array` | | ||
| `contains` | | checks whether field's value contains the provided value | `array` | `any` | | ||
| `length` | `len` | checks whether field's value length equals the provided value | `array` | `integer` | | ||
| `minLength` | `minlen` | checks whether field's value has minimum length equals the provided value | `array` | `integer` | | ||
| `maxLength` | `maxlen` | checks whether field's value has maximum length equals the provided value | `array` | `integer` | |
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,13 @@ | ||
module github.com/ahsayde/yapl | ||
|
||
go 1.18 | ||
|
||
require ( | ||
github.com/stretchr/testify v1.8.0 | ||
gopkg.in/yaml.v3 v3.0.1 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
) |
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,15 @@ | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= | ||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
Oops, something went wrong.