Skip to content

Commit 7004f73

Browse files
authored
Feat bundle matcher languages harmonization (#110)
* feat(bundle): package matcher harmonization. * doc(changelog): update changelog.
1 parent c340053 commit 7004f73

File tree

11 files changed

+462
-120
lines changed

11 files changed

+462
-120
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,28 @@
44

55
FEATURES:
66

7+
* template/engine:
8+
* `isodate` time formatter to RFC3389 date format.
9+
* bundle/pipeline:
10+
* Support custom input reader and output writer. [#105](https://github.com/elastic/harp/pull/105)
11+
* bundle/selector:
12+
* support `glob` for package path and secret key matcher. [#110](https://github.com/elastic/harp/pull/110)
13+
* support `rego` policy for `bunde filter` command and `BundlePatch` selector. [#106](https://github.com/elastic/harp/pull/106)
14+
* support `cel` expressions used in `BundleRuleSet` for package matchers in `bundle filter` command and `BundlePatch` selector. [#109](https://github.com/elastic/harp/pull/109)
715
* sdk/value:
816
* support `age` encryption as value transformer. [#102](https://github.com/elastic/harp/pull/102)
917
* support deterministic authenticated encryption value transformers. [#103](https://github.com/elastic/harp/pull/103)
1018
* support additional data for AEAD/DAE transformers. [#104](https://github.com/elastic/harp/pull/104)
1119
* DAE transformers can be initialized using an optional salt to derive different keys from the transformer key. [#104](https://github.com/elastic/harp/pull/104)
1220

21+
DIST
22+
23+
* go: Build with Golang 1.17.6.
24+
* build/ci
25+
* Add SLSA Level 1 - Provenance generation step for binaries.
26+
* Add Snyk as code / dependencies scanner via SARIF.
27+
* Add Trivy dependencies scanner via SARIF.
28+
1329
## 0.2.5
1430

1531
### 2022-01-28

api/gen/go/harp/bundle/v1/patch.pb.go

Lines changed: 100 additions & 78 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/proto/harp/bundle/v1/patch.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ message PatchSelectorMatchPath {
9595
// Regex path matching.
9696
// Value can be templatized.
9797
string regex = 2;
98+
// Glob path matching. - https://github.com/gobwas/glob
99+
// Value can be templatized.
100+
string glob = 3;
98101
}
99102

100103
// PatchSelectorMatchPath represents package path matching strategies.
@@ -105,6 +108,9 @@ message PatchSelectorMatchSecret {
105108
// Regex secret matching.
106109
// Value can be templatized.
107110
string regex = 2;
111+
// Glob path matching. - https://github.com/gobwas/glob
112+
// Value can be templatized.
113+
string glob = 3;
108114
}
109115

110116
// PatchPackagePath represents package path operations.

docs/onboarding/3-secret-bundle/4-patch.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ bundle source without altering the source bundle.
2121
- [Match by regex path](#match-by-regex-path)
2222
- [Match by JMES filter](#match-by-jmes-filter)
2323
- [Match by Rego policy](#match-by-rego-policy)
24+
- [Match by CEL expression](#match-by-cel-expression)
2425
- [Match by secret key](#match-by-secret-key)
2526
- [PatchSelectorMatchPath](#patchselectormatchpath)
2627
- [PatchPackage](#patchpackage)
@@ -187,6 +188,8 @@ message PatchSelector {
187188
string rego = 3;
188189
// Match a package by secret.
189190
PatchSelectorMatchSecret matchSecret = 4;
191+
// Match a package using CEL expressions.
192+
repeated string cel = 5;
190193
}
191194
```
192195

@@ -278,6 +281,43 @@ spec:
278281
279282
```
280283

284+
#### Match by CEL expression
285+
286+
```yaml
287+
selector:
288+
cel:
289+
- "p.is_cso_compliant()"
290+
```
291+
292+
Sample use case
293+
294+
```yaml
295+
apiVersion: harp.elastic.co/v1
296+
kind: BundlePatch
297+
meta:
298+
name: "cso-compliance-flagger"
299+
owner: security@elastic.co
300+
description: "Flag non CSO complaint packages"
301+
spec:
302+
rules:
303+
- selector:
304+
cel:
305+
# No CSO compliant path as package name
306+
- "!p.is_cso_compliant()"
307+
package:
308+
labels:
309+
add:
310+
cso-compliant: false
311+
- selector:
312+
cel:
313+
# CSO compliant path as package name
314+
- "p.is_cso_compliant()"
315+
package:
316+
labels:
317+
add:
318+
cso-compliant: true
319+
```
320+
281321
#### Match by secret key
282322

283323
Strict matcher
@@ -323,6 +363,7 @@ spec:
323363

324364
* `strict` is used to filter package path when strictly equal to the given value
325365
* `regex` is used to match the package path with the given regular expression
366+
* `glob` is used to match the package path with the given glob expression
326367

327368
```cpp
328369
// PatchSelectorMatchPath represents package path matching strategies.
@@ -333,6 +374,9 @@ message PatchSelectorMatchPath {
333374
// Regex path matching.
334375
// Value can be templatized.
335376
string regex = 2;
377+
// Glob path matching. - https://github.com/gobwas/glob
378+
// Value can be templatized.
379+
string glob = 3;
336380
}
337381
```
338382

docs/onboarding/3-secret-bundle/5-ruleset.md

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
1-
<!--
2-
Copyright 2022 Thibault NORMAND
1+
# RuleSet
32

4-
Licensed under the Apache License, Version 2.0 (the "License");
5-
you may not use this file except in compliance with the License.
6-
You may obtain a copy of the License at
3+
- [RuleSet](#ruleset)
4+
- [Query language](#query-language)
5+
- [CEL Expressions](#cel-expressions)
6+
- [Package matchers](#package-matchers)
7+
- [Secret context](#secret-context)
78

8-
http://www.apache.org/licenses/LICENSE-2.0
9+
## Query language
10+
### CEL Expressions
911

10-
Unless required by applicable law or agreed to in writing, software
11-
distributed under the License is distributed on an "AS IS" BASIS,
12-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
See the License for the specific language governing permissions and
14-
limitations under the License.
15-
-->
12+
#### Package matchers
13+
14+
* `p.match_path(globstring) bool` - Returns true if the package name match the given Glob pattern.
15+
* `p.match_label(globstring) bool` - Returns true if one of the package labels match the given Glob pattern.
16+
* `p.match_annotation(globstring) bool` - Returns true if one of the package annotations match the given Glob pattern.
17+
* `p.match_secret(globstring) bool` - Returns true if the package has a secret with given pattern.
18+
* `p.has_secret(string) bool` - Returns true if the package has a secret with given key.
19+
* `p.has_all_secrets(...string) bool` - Returns true if the package has all secrets with given keys.
20+
* `p.is_cso_compliant() bool` - Returns true is the package name is CSO compliant.
21+
22+
#### Secret context
23+
24+
* `p.secret(string) Secret` - Returns the secret context matching the secret key of the package.
25+
* `p.secret(string).is_required()` - Flag the given secret key as required.
26+
* `p.secret(string).is_base64()` - Flag the given secret value has a valid base64 encoded string.
27+
* `p.secret(string).is_url()` - Flag the given secret value as a valid URL.
28+
* `p.secret(string).is_uuid()` - Flag the given secret value as a valid UUID.
29+
* `p.secret(string).is_email()` - Flag the given secret value as a valid email.
30+
* `p.secret(string).is_json()` - Flag the given secret value as a valid JSON.
1631

1732
---
1833

pkg/bundle/patch/executor.go

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package patch
2020
import (
2121
"context"
2222
"encoding/json"
23+
"errors"
2324
"fmt"
2425
"regexp"
2526

@@ -80,7 +81,7 @@ func executeRule(r *bundlev1.PatchRule, p *bundlev1.Package, values map[string]i
8081
return packageUnchanged, nil
8182
}
8283

83-
//nolint:gocyclo // to refactor
84+
//nolint:gocyclo,funlen // to refactor
8485
func compileSelector(s *bundlev1.PatchSelector, values map[string]interface{}) (selector.Specification, error) {
8586
// Check parameters
8687
if s == nil {
@@ -89,7 +90,8 @@ func compileSelector(s *bundlev1.PatchSelector, values map[string]interface{}) (
8990

9091
// Has matchPath selector
9192
if s.MatchPath != nil {
92-
if s.MatchPath.Strict != "" {
93+
switch {
94+
case s.MatchPath.Strict != "":
9395
// Evaluation with template engine first
9496
value, err := engine.Render(s.MatchPath.Strict, map[string]interface{}{
9597
"Values": values,
@@ -100,24 +102,30 @@ func compileSelector(s *bundlev1.PatchSelector, values map[string]interface{}) (
100102

101103
// Return specification
102104
return selector.MatchPathStrict(value), nil
103-
}
104-
if s.MatchPath.Regex != "" {
105+
case s.MatchPath.Glob != "":
105106
// Evaluation with template engine first
106-
value, err := engine.Render(s.MatchPath.Regex, map[string]interface{}{
107+
value, err := engine.Render(s.MatchPath.Glob, map[string]interface{}{
107108
"Values": values,
108109
})
109110
if err != nil {
110111
return nil, fmt.Errorf("unable to evaluate template before matchPath build: %w", err)
111112
}
112113

113-
// Compile regexp
114-
re, err := regexp.Compile(value)
114+
// Return specification
115+
return selector.MatchPathGlob(value)
116+
case s.MatchPath.Regex != "":
117+
// Evaluation with template engine first
118+
value, err := engine.Render(s.MatchPath.Regex, map[string]interface{}{
119+
"Values": values,
120+
})
115121
if err != nil {
116-
return nil, fmt.Errorf("unable to compile macthPath regexp `%s`: %w", s.MatchPath.Regex, err)
122+
return nil, fmt.Errorf("unable to evaluate template before matchPath build: %w", err)
117123
}
118124

119125
// Return specification
120-
return selector.MatchPathRegex(re), nil
126+
return selector.MatchPathRegex(value)
127+
default:
128+
return nil, errors.New("no strict, glob or regexp defined for path matcher")
121129
}
122130
}
123131

@@ -135,7 +143,8 @@ func compileSelector(s *bundlev1.PatchSelector, values map[string]interface{}) (
135143

136144
// Has matchSecret selector
137145
if s.MatchSecret != nil {
138-
if s.MatchSecret.Strict != "" {
146+
switch {
147+
case s.MatchSecret.Strict != "":
139148
// Evaluation with template engine first
140149
value, err := engine.Render(s.MatchSecret.Strict, map[string]interface{}{
141150
"Values": values,
@@ -146,8 +155,18 @@ func compileSelector(s *bundlev1.PatchSelector, values map[string]interface{}) (
146155

147156
// Return specification
148157
return selector.MatchSecretStrict(value), nil
149-
}
150-
if s.MatchSecret.Regex != "" {
158+
case s.MatchSecret.Glob != "":
159+
// Evaluation with template engine first
160+
value, err := engine.Render(s.MatchSecret.Glob, map[string]interface{}{
161+
"Values": values,
162+
})
163+
if err != nil {
164+
return nil, fmt.Errorf("unable to evaluate template before matchSecret build: %w", err)
165+
}
166+
167+
// Return specification
168+
return selector.MatchSecretGlob(value), nil
169+
case s.MatchSecret.Regex != "":
151170
// Evaluation with template engine first
152171
value, err := engine.Render(s.MatchSecret.Regex, map[string]interface{}{
153172
"Values": values,
@@ -164,6 +183,8 @@ func compileSelector(s *bundlev1.PatchSelector, values map[string]interface{}) (
164183

165184
// Return specification
166185
return selector.MatchSecretRegex(re), nil
186+
default:
187+
return nil, errors.New("no strict, glob or regexp defined for secret matcher")
167188
}
168189
}
169190

0 commit comments

Comments
 (0)