Skip to content
Open
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
4 changes: 4 additions & 0 deletions database/migrations/000117_rule_type_rego_version.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- SPDX-FileCopyrightText: Copyright 2024 The Minder Authors
-- SPDX-License-Identifier: Apache-2.0

ALTER TABLE rule_type DROP COLUMN rego_version;
6 changes: 6 additions & 0 deletions database/migrations/000117_rule_type_rego_version.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- SPDX-FileCopyrightText: Copyright 2024 The Minder Authors
-- SPDX-License-Identifier: Apache-2.0

-- Add rego_version column to rule_type table to cache the detected Rego
-- language version. All existing rule types are assumed to be V0.
ALTER TABLE rule_type ADD COLUMN rego_version TEXT NOT NULL DEFAULT 'v0';
8 changes: 5 additions & 3 deletions database/query/rule_types.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ INSERT INTO rule_type (
subscription_id,
display_name,
release_phase,
short_failure_message
short_failure_message,
rego_version
) VALUES (
$1,
$2,
Expand All @@ -20,7 +21,8 @@ INSERT INTO rule_type (
sqlc.narg(subscription_id),
sqlc.arg(display_name),
sqlc.arg(release_phase),
sqlc.arg(short_failure_message)
sqlc.arg(short_failure_message),
sqlc.arg(rego_version)
) RETURNING *;

-- name: ListRuleTypesByProject :many
Expand All @@ -37,7 +39,7 @@ DELETE FROM rule_type WHERE id = $1;

-- name: UpdateRuleType :one
UPDATE rule_type
SET description = $2, definition = sqlc.arg(definition)::jsonb, severity_value = sqlc.arg(severity_value), display_name = sqlc.arg(display_name), release_phase = sqlc.arg(release_phase), short_failure_message = sqlc.arg(short_failure_message)
SET description = $2, definition = sqlc.arg(definition)::jsonb, severity_value = sqlc.arg(severity_value), display_name = sqlc.arg(display_name), release_phase = sqlc.arg(release_phase), short_failure_message = sqlc.arg(short_failure_message), rego_version = sqlc.arg(rego_version)
WHERE id = $1
RETURNING *;

Expand Down
1 change: 1 addition & 0 deletions internal/db/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 21 additions & 9 deletions internal/db/rule_types.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 17 additions & 4 deletions internal/engine/eval/rego/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Evaluator struct {
regoOpts []func(*rego.Rego)
reseval resultEvaluator
datasources *v1datasources.DataSourceRegistry
regoVersion ast.RegoVersion
}

// Input is the input for the rego evaluator
Expand Down Expand Up @@ -83,8 +84,9 @@ func NewRegoEvaluator(
re := c.getEvalType()

eval := &Evaluator{
cfg: c,
reseval: re,
cfg: c,
reseval: re,
regoVersion: ast.RegoV0,
regoOpts: []func(*rego.Rego){
rego.Query(RegoQueryPrefix),
rego.Module(MinderRegoFile, c.Def),
Expand Down Expand Up @@ -140,8 +142,7 @@ func (e *Evaluator) Eval(

// Register options to expose functions
regoFuncOptions := []func(*rego.Rego){
// TODO: figure out a Rego V1 migration path (https://github.com/mindersec/minder/issues/5262)
rego.SetRegoVersion(ast.RegoV0),
rego.SetRegoVersion(e.regoVersion),
}

// Initialize the built-in minder library rego functions
Expand Down Expand Up @@ -188,6 +189,18 @@ func enrichInputWithEntityProps(
}
}

// WithRegoVersion returns an Option that sets the Rego language version used
// for evaluation. The version is typically determined at rule type creation
// time via dual-parse detection and stored in the database.
func WithRegoVersion(v ast.RegoVersion) interfaces.Option {
return func(eval interfaces.Evaluator) error {
if e, ok := eval.(*Evaluator); ok {
e.regoVersion = v
}
return nil
}
}

// WithShortFailureMessage returns an Option that sets the short failure message for deny-by-default evaluations.
// This message will be used as a fallback when the rego policy doesn't provide a custom "message" field,
// but before defaulting to the generic "denied" message.
Expand Down
40 changes: 40 additions & 0 deletions internal/engine/eval/rego/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2024 The Minder Authors
// SPDX-License-Identifier: Apache-2.0

package rego

import (
"github.com/open-policy-agent/opa/v1/ast"
)

// DetectRegoVersion attempts to parse the Rego source with the V1 parser first.
// If V1 parsing succeeds, it returns ast.RegoV1. If V1 parsing fails, it falls
// back to ast.RegoV0. This allows the system to accept both V0 and V1 policies
// without requiring any user-facing changes.
func DetectRegoVersion(def string) ast.RegoVersion {
_, err := ast.ParseModuleWithOpts(MinderRegoFile, def,
ast.ParserOptions{RegoVersion: ast.RegoV1})
if err == nil {
return ast.RegoV1
}

return ast.RegoV0
}

// VersionToString converts an ast.RegoVersion to the string stored in the
// database.
func VersionToString(v ast.RegoVersion) string {
if v == ast.RegoV1 {
return "v1"
}
return "v0"
}

// VersionFromString converts a stored database string back to an
// ast.RegoVersion.
func VersionFromString(s string) ast.RegoVersion {
if s == "v1" {
return ast.RegoV1
}
return ast.RegoV0
}
Loading
Loading