feat: add metadata enforcement (#1990)

* fix(controller): decode old object for delete requests

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix: preserve ca-bundles injected from external providers

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat: add metadata enforcement

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat: add metadata enforcement

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Oliver Bähler
2026-07-02 13:42:08 +02:00
committed by GitHub
co-authored by Copilot Autofix powered by AI
parent 6fbd472f27
commit 77d1810bb9
89 changed files with 12731 additions and 556 deletions
+4 -4
View File
@@ -11,7 +11,7 @@ import (
"strings"
"sync"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/runtime"
)
type CompiledRegex struct {
@@ -46,7 +46,7 @@ func NewRegexCache() *RegexCache {
}
}
func (c *RegexCache) GetOrCompile(expr api.ExpressionRegex) (*CompiledRegex, bool, error) {
func (c *RegexCache) GetOrCompile(expr runtime.ExpressionRegex) (*CompiledRegex, bool, error) {
if c == nil {
return nil, false, fmt.Errorf("regex cache is nil")
}
@@ -129,7 +129,7 @@ func (c *RegexCache) Reset() {
c.re = make(map[string]*CompiledRegex)
}
func (c *RegexCache) MatchRegex(expr api.ExpressionRegex, value string) (bool, error) {
func (c *RegexCache) MatchRegex(expr runtime.ExpressionRegex, value string) (bool, error) {
compiled, _, err := c.GetOrCompile(expr)
if err != nil {
return false, err
@@ -138,7 +138,7 @@ func (c *RegexCache) MatchRegex(expr api.ExpressionRegex, value string) (bool, e
return compiled.MatchString(value), nil
}
func HashRegex(expr api.ExpressionRegex) string {
func HashRegex(expr runtime.ExpressionRegex) string {
var b strings.Builder
b.WriteString(strings.TrimSpace(expr.Expression))
+17 -17
View File
@@ -6,7 +6,7 @@ package cache
import (
"testing"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/runtime"
)
func TestCompiledRegexMatchString(t *testing.T) {
@@ -14,13 +14,13 @@ func TestCompiledRegexMatchString(t *testing.T) {
tests := []struct {
name string
expression api.ExpressionRegex
expression runtime.ExpressionRegex
value string
want bool
}{
{
name: "normal expression matches matching value",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: "trusted/.*",
},
value: "trusted/team/app:1",
@@ -28,7 +28,7 @@ func TestCompiledRegexMatchString(t *testing.T) {
},
{
name: "normal expression does not match non matching value",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: "trusted/.*",
},
value: "docker.io/team/app:1",
@@ -36,7 +36,7 @@ func TestCompiledRegexMatchString(t *testing.T) {
},
{
name: "negated expression does not match matching value",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
},
@@ -45,7 +45,7 @@ func TestCompiledRegexMatchString(t *testing.T) {
},
{
name: "negated expression matches non matching value",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
},
@@ -79,7 +79,7 @@ func TestRegexCache_GetOrCompile(t *testing.T) {
tests := []struct {
name string
expression api.ExpressionRegex
expression runtime.ExpressionRegex
value string
wantMatch bool
wantErr bool
@@ -88,7 +88,7 @@ func TestRegexCache_GetOrCompile(t *testing.T) {
}{
{
name: "compile matching regex",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: `^ghcr\.io/projectcapsule/.*`,
},
value: "ghcr.io/projectcapsule/capsule:latest",
@@ -99,7 +99,7 @@ func TestRegexCache_GetOrCompile(t *testing.T) {
},
{
name: "compile non matching regex",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: `^ghcr\.io/projectcapsule/.*`,
},
value: "docker.io/library/nginx:latest",
@@ -110,7 +110,7 @@ func TestRegexCache_GetOrCompile(t *testing.T) {
},
{
name: "compile negated matching regex",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: `^ghcr\.io/projectcapsule/.*`,
Negate: true,
},
@@ -122,7 +122,7 @@ func TestRegexCache_GetOrCompile(t *testing.T) {
},
{
name: "compile negated non matching regex",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: `^ghcr\.io/projectcapsule/.*`,
Negate: true,
},
@@ -134,7 +134,7 @@ func TestRegexCache_GetOrCompile(t *testing.T) {
},
{
name: "reject empty expression",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: "",
},
value: "ghcr.io/projectcapsule/capsule:latest",
@@ -143,7 +143,7 @@ func TestRegexCache_GetOrCompile(t *testing.T) {
},
{
name: "reject invalid regex",
expression: api.ExpressionRegex{
expression: runtime.ExpressionRegex{
Expression: `[`,
},
value: "ghcr.io/projectcapsule/capsule:latest",
@@ -207,7 +207,7 @@ func TestRegexCache_GetOrCompile_ReusesCachedRegex(t *testing.T) {
c := NewRegexCache()
expr := api.ExpressionRegex{
expr := runtime.ExpressionRegex{
Expression: `^ghcr\.io/projectcapsule/.*`,
}
@@ -241,11 +241,11 @@ func TestRegexCache_GetOrCompile_ReusesCachedRegex(t *testing.T) {
func TestRegexCache_HashRegex_UsesNegate(t *testing.T) {
t.Parallel()
positive := HashRegex(api.ExpressionRegex{
positive := HashRegex(runtime.ExpressionRegex{
Expression: `^ghcr\.io/.*`,
})
negative := HashRegex(api.ExpressionRegex{
negative := HashRegex(runtime.ExpressionRegex{
Expression: `^ghcr\.io/.*`,
Negate: true,
})
@@ -260,7 +260,7 @@ func TestRegexCache_Reset(t *testing.T) {
c := NewRegexCache()
compiled, _, err := c.GetOrCompile(api.ExpressionRegex{
compiled, _, err := c.GetOrCompile(runtime.ExpressionRegex{
Expression: `^ghcr\.io/.*`,
})
if err != nil {
+2 -2
View File
@@ -13,8 +13,8 @@ import (
corev1 "k8s.io/api/core/v1"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/rules"
"github.com/projectcapsule/capsule/pkg/api/runtime"
)
type RuleSet struct {
@@ -23,7 +23,7 @@ type RuleSet struct {
}
type CompiledRule struct {
Match api.ExpressionMatch
Match runtime.ExpressionMatch
// RegexID is empty when Match.Expression is empty.
RegexID string
+19 -19
View File
@@ -10,8 +10,8 @@ import (
corev1 "k8s.io/api/core/v1"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/rules"
"github.com/projectcapsule/capsule/pkg/api/runtime"
)
func TestNewRegistryRuleSetCache(t *testing.T) {
@@ -94,7 +94,7 @@ func TestRegistryRuleSetCacheGetOrBuild(t *testing.T) {
{
name: "registry with negated expression builds ruleset",
rules: []rules.OCIRegistry{
registryWithExpression(api.ExpressionRegex{
registryWithExpression(runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
}),
@@ -242,7 +242,7 @@ func TestRegistryRuleSetCacheBuildRuleSet(t *testing.T) {
specRules := []rules.OCIRegistry{
registry("harbor/.*"),
registryWithPolicy("ghcr.io/.*", corev1.PullAlways, corev1.PullIfNotPresent),
registryWithExpression(api.ExpressionRegex{
registryWithExpression(runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
}),
@@ -363,7 +363,7 @@ func TestRegistryRuleSetCacheMatchReference(t *testing.T) {
{
name: "negated expression matches non-matching reference",
rules: []rules.OCIRegistry{
registryWithExpression(api.ExpressionRegex{
registryWithExpression(runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
}),
@@ -377,7 +377,7 @@ func TestRegistryRuleSetCacheMatchReference(t *testing.T) {
{
name: "negated expression does not match matching reference",
rules: []rules.OCIRegistry{
registryWithExpression(api.ExpressionRegex{
registryWithExpression(runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
}),
@@ -406,7 +406,7 @@ func TestRegistryRuleSetCacheMatchReference(t *testing.T) {
{
name: "nested regex expression wins over legacy url",
rules: []rules.OCIRegistry{
registryWithExpression(api.ExpressionRegex{
registryWithExpression(runtime.ExpressionRegex{
Expression: "nested/.*",
}),
},
@@ -417,7 +417,7 @@ func TestRegistryRuleSetCacheMatchReference(t *testing.T) {
{
name: "legacy url is ignored when nested regex expression is set",
rules: []rules.OCIRegistry{
registryWithExpression(api.ExpressionRegex{
registryWithExpression(runtime.ExpressionRegex{
Expression: "nested/.*",
}),
},
@@ -526,7 +526,7 @@ func TestRegistryRuleSetCacheMatchRuleSetWithPullPolicy(t *testing.T) {
{
name: "negated expression respects pull policy",
rules: []rules.OCIRegistry{
registryWithExpressionAndPolicy(api.ExpressionRegex{
registryWithExpressionAndPolicy(runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
}, corev1.PullIfNotPresent),
@@ -541,7 +541,7 @@ func TestRegistryRuleSetCacheMatchRuleSetWithPullPolicy(t *testing.T) {
{
name: "negated expression still rejects forbidden pull policy",
rules: []rules.OCIRegistry{
registryWithExpressionAndPolicy(api.ExpressionRegex{
registryWithExpressionAndPolicy(runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
}, corev1.PullNever),
@@ -714,14 +714,14 @@ func TestRegistryRuleSetCacheHashRules(t *testing.T) {
c := NewRegistryRuleSetCache(nil)
hashA := c.HashRules([]rules.OCIRegistry{
registryWithExpression(api.ExpressionRegex{
registryWithExpression(runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: false,
}),
})
hashB := c.HashRules([]rules.OCIRegistry{
registryWithExpression(api.ExpressionRegex{
registryWithExpression(runtime.ExpressionRegex{
Expression: "trusted/.*",
Negate: true,
}),
@@ -924,8 +924,8 @@ func TestRegistryRuleSetCacheInsertForTest(t *testing.T) {
func registry(expression string) rules.OCIRegistry {
return rules.OCIRegistry{
ExpressionMatch: api.ExpressionMatch{
ExpressionRegex: api.ExpressionRegex{
ExpressionMatch: runtime.ExpressionMatch{
ExpressionRegex: runtime.ExpressionRegex{
Expression: expression,
Negate: false,
},
@@ -935,8 +935,8 @@ func registry(expression string) rules.OCIRegistry {
func registryWithPolicy(expression string, policies ...corev1.PullPolicy) rules.OCIRegistry {
return rules.OCIRegistry{
ExpressionMatch: api.ExpressionMatch{
ExpressionRegex: api.ExpressionRegex{
ExpressionMatch: runtime.ExpressionMatch{
ExpressionRegex: runtime.ExpressionRegex{
Expression: expression,
Negate: false,
},
@@ -945,20 +945,20 @@ func registryWithPolicy(expression string, policies ...corev1.PullPolicy) rules.
}
}
func registryWithExpression(expression api.ExpressionRegex) rules.OCIRegistry {
func registryWithExpression(expression runtime.ExpressionRegex) rules.OCIRegistry {
return rules.OCIRegistry{
ExpressionMatch: api.ExpressionMatch{
ExpressionMatch: runtime.ExpressionMatch{
ExpressionRegex: expression,
},
}
}
func registryWithExpressionAndPolicy(
expression api.ExpressionRegex,
expression runtime.ExpressionRegex,
policies ...corev1.PullPolicy,
) rules.OCIRegistry {
return rules.OCIRegistry{
ExpressionMatch: api.ExpressionMatch{
ExpressionMatch: runtime.ExpressionMatch{
ExpressionRegex: expression,
},
Policy: policies,