From 48021041a58d8f452a32aa2328d6db0bc9662819 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Wed, 20 Aug 2025 15:42:27 +0200 Subject: [PATCH 1/6] add enable print flag Signed-off-by: Sylwester Piskozub --- app/cli/cmd/policy_develop_eval.go | 3 ++ .../internal/action/policy_develop_eval.go | 2 ++ app/cli/internal/policydevel/eval.go | 6 ++-- pkg/policies/engine/rego/rego.go | 36 +++++++++++++++++-- pkg/policies/policies.go | 13 +++++++ 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/app/cli/cmd/policy_develop_eval.go b/app/cli/cmd/policy_develop_eval.go index 4aae0d702..6ba10ecd2 100644 --- a/app/cli/cmd/policy_develop_eval.go +++ b/app/cli/cmd/policy_develop_eval.go @@ -33,6 +33,7 @@ func newPolicyDevelopEvalCmd() *cobra.Command { inputs []string allowedHostnames []string debug bool + enablePrint bool ) cmd := &cobra.Command{ @@ -53,6 +54,7 @@ evaluates the policy against the provided material or attestation.`, Inputs: parseKeyValue(inputs), AllowedHostnames: allowedHostnames, Debug: debug, + EnablePrint: enablePrint, } policyEval, err := action.NewPolicyEval(opts, actionOpts) @@ -77,6 +79,7 @@ evaluates the policy against the provided material or attestation.`, cmd.Flags().StringArrayVar(&inputs, "input", []string{}, "Key-value pairs of policy inputs (key=value)") cmd.Flags().StringSliceVar(&allowedHostnames, "allowed-hostnames", []string{}, "Additional hostnames allowed for http.send requests in policies") cmd.Flags().BoolVarP(&debug, "debug", "", false, "Include detailed evaluation inputs/outputs in JSON output and enable verbose logging") + cmd.Flags().BoolVar(&enablePrint, "enable-print", false, "Enable print statements in Rego policies") return cmd } diff --git a/app/cli/internal/action/policy_develop_eval.go b/app/cli/internal/action/policy_develop_eval.go index 708e28446..6684efb24 100644 --- a/app/cli/internal/action/policy_develop_eval.go +++ b/app/cli/internal/action/policy_develop_eval.go @@ -27,6 +27,7 @@ type PolicyEvalOpts struct { Inputs map[string]string AllowedHostnames []string Debug bool + EnablePrint bool } type PolicyEval struct { @@ -50,6 +51,7 @@ func (action *PolicyEval) Run() (*policydevel.EvalSummary, error) { Inputs: action.opts.Inputs, AllowedHostnames: action.opts.AllowedHostnames, Debug: action.opts.Debug, + EnablePrint: action.opts.EnablePrint, } // Evaluate policy diff --git a/app/cli/internal/policydevel/eval.go b/app/cli/internal/policydevel/eval.go index 92fbb177e..782a1ac9b 100644 --- a/app/cli/internal/policydevel/eval.go +++ b/app/cli/internal/policydevel/eval.go @@ -37,6 +37,7 @@ type EvalOptions struct { Inputs map[string]string AllowedHostnames []string Debug bool + EnablePrint bool } type EvalResult struct { @@ -70,7 +71,7 @@ func Evaluate(opts *EvalOptions, logger zerolog.Logger) (*EvalSummary, error) { material.Annotations = opts.Annotations // 3. Verify material against policy - summary, err := verifyMaterial(schema, material, opts.MaterialPath, opts.Debug, opts.AllowedHostnames, &logger) + summary, err := verifyMaterial(schema, material, opts.MaterialPath, opts.Debug, opts.EnablePrint, opts.AllowedHostnames, &logger) if err != nil { return nil, err } @@ -93,13 +94,14 @@ func createCraftingSchema(policyPath string, inputs map[string]string) (*v1.Craf }, nil } -func verifyMaterial(schema *v1.CraftingSchema, material *v12.Attestation_Material, materialPath string, debug bool, allowedHostnames []string, logger *zerolog.Logger) (*EvalSummary, error) { +func verifyMaterial(schema *v1.CraftingSchema, material *v12.Attestation_Material, materialPath string, debug, enablePrint bool, allowedHostnames []string, logger *zerolog.Logger) (*EvalSummary, error) { var opts []policies.PolicyVerifierOption if len(allowedHostnames) > 0 { opts = append(opts, policies.WithAllowedHostnames(allowedHostnames...)) } opts = append(opts, policies.WithIncludeRawData(debug)) + opts = append(opts, policies.WithEnablePrint(enablePrint)) v := policies.NewPolicyVerifier(schema, nil, logger, opts...) policyEvs, err := v.VerifyMaterial(context.Background(), material, materialPath) diff --git a/pkg/policies/engine/rego/rego.go b/pkg/policies/engine/rego/rego.go index a71e018fa..b3a518379 100644 --- a/pkg/policies/engine/rego/rego.go +++ b/pkg/policies/engine/rego/rego.go @@ -24,6 +24,7 @@ import ( "github.com/chainloop-dev/chainloop/pkg/policies/engine" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" + "github.com/open-policy-agent/opa/topdown/print" "golang.org/x/exp/maps" ) @@ -37,6 +38,8 @@ type Engine struct { allowedNetworkDomains []string // includeRawData determines whether to collect raw evaluation data includeRawData bool + // enablePrint determines whether to enable print statements in rego policies + enablePrint bool } type EngineOption func(*newEngineOptions) @@ -59,10 +62,17 @@ func WithIncludeRawData(include bool) EngineOption { } } +func WithEnablePrint(enable bool) EngineOption { + return func(e *newEngineOptions) { + e.enablePrint = enable + } +} + type newEngineOptions struct { operatingMode EnvironmentMode allowedNetworkDomains []string includeRawData bool + enablePrint bool } // NewEngine creates a new policy engine with the given options @@ -88,6 +98,7 @@ func NewEngine(opts ...EngineOption) *Engine { // append base allowed network domains to the user provided ones allowedNetworkDomains: append(baseAllowedNetworkDomains, options.allowedNetworkDomains...), includeRawData: options.includeRawData, + enablePrint: options.enablePrint, } } @@ -112,6 +123,15 @@ var builtinFuncNotAllowed = []*ast.Builtin{ ast.Trace, } +// Implements the OPA print.Hook interface to capture and output +// print statements from Rego policies during evaluation. +type printHook struct{} + +func (p *printHook) Print(ctx print.Context, msg string) error { + fmt.Println(msg) + return nil +} + // Force interface var _ engine.PolicyEngine = (*Engine)(nil) @@ -156,11 +176,21 @@ func (r *Engine) Verify(ctx context.Context, policy *engine.Policy, input []byte var res rego.ResultSet // Function to execute the query with appropriate parameters executeQuery := func(rule string, strict bool) error { + options := []func(r *rego.Rego){regoInput, regoFunc, rego.Capabilities(r.Capabilities())} + + // Add print support if enabled + if r.enablePrint { + options = append(options, + rego.EnablePrintStatements(true), + rego.PrintHook(&printHook{}), + ) + } + if strict { - res, err = queryRego(ctx, rule, regoInput, regoFunc, rego.Capabilities(r.Capabilities()), rego.StrictBuiltinErrors(true)) - } else { - res, err = queryRego(ctx, rule, regoInput, regoFunc, rego.Capabilities(r.Capabilities())) + options = append(options, rego.StrictBuiltinErrors(true)) } + + res, err = queryRego(ctx, rule, options...) return err } diff --git a/pkg/policies/policies.go b/pkg/policies/policies.go index d9c72b148..7b173bbf6 100644 --- a/pkg/policies/policies.go +++ b/pkg/policies/policies.go @@ -66,6 +66,7 @@ type PolicyVerifier struct { client v13.AttestationServiceClient allowedHostnames []string includeRawData bool + enablePrint bool } var _ Verifier = (*PolicyVerifier)(nil) @@ -73,6 +74,7 @@ var _ Verifier = (*PolicyVerifier)(nil) type PolicyVerifierOptions struct { AllowedHostnames []string IncludeRawData bool + EnablePrint bool } type PolicyVerifierOption func(*PolicyVerifierOptions) @@ -89,6 +91,12 @@ func WithIncludeRawData(include bool) PolicyVerifierOption { } } +func WithEnablePrint(enable bool) PolicyVerifierOption { + return func(o *PolicyVerifierOptions) { + o.EnablePrint = enable + } +} + func NewPolicyVerifier(schema *v1.CraftingSchema, client v13.AttestationServiceClient, logger *zerolog.Logger, opts ...PolicyVerifierOption) *PolicyVerifier { options := &PolicyVerifierOptions{} for _, opt := range opts { @@ -101,6 +109,7 @@ func NewPolicyVerifier(schema *v1.CraftingSchema, client v13.AttestationServiceC logger: logger, allowedHostnames: options.AllowedHostnames, includeRawData: options.IncludeRawData, + enablePrint: options.EnablePrint, } } @@ -349,6 +358,10 @@ func (pv *PolicyVerifier) executeScript(ctx context.Context, script *engine.Poli engineOpts = append(engineOpts, rego.WithIncludeRawData(true)) } + if pv.enablePrint { + engineOpts = append(engineOpts, rego.WithEnablePrint(true)) + } + // verify the policy ng := rego.NewEngine(engineOpts...) res, err := ng.Verify(ctx, script, material, getInputArguments(args)) From 60642c98e41f98ae4c56dc09bf698082ee9326bd Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Wed, 20 Aug 2025 16:14:30 +0200 Subject: [PATCH 2/6] fix lint Signed-off-by: Sylwester Piskozub --- app/cli/documentation/cli-reference.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/cli/documentation/cli-reference.mdx b/app/cli/documentation/cli-reference.mdx index 90386a335..cc90df65e 100755 --- a/app/cli/documentation/cli-reference.mdx +++ b/app/cli/documentation/cli-reference.mdx @@ -2848,6 +2848,7 @@ Options --allowed-hostnames strings Additional hostnames allowed for http.send requests in policies --annotation strings Key-value pairs of material annotations (key=value) --debug Include detailed evaluation inputs/outputs in JSON output and enable verbose logging +--enable-print Enable print statements in Rego policies -h, --help help for eval --input stringArray Key-value pairs of policy inputs (key=value) --kind string Kind of the material: ["ARTIFACT" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CHAINLOOP_RUNNER_CONTEXT" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENVEX" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "TWISTCLI_SCAN_JSON" "ZAP_DAST_ZIP"] From 77ef3ec09fc99d04a4c8e5734e6d1198a94391f0 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Wed, 20 Aug 2025 16:14:38 +0200 Subject: [PATCH 3/6] fix lint Signed-off-by: Sylwester Piskozub --- pkg/policies/engine/rego/rego.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/policies/engine/rego/rego.go b/pkg/policies/engine/rego/rego.go index b3a518379..c69c44032 100644 --- a/pkg/policies/engine/rego/rego.go +++ b/pkg/policies/engine/rego/rego.go @@ -24,7 +24,7 @@ import ( "github.com/chainloop-dev/chainloop/pkg/policies/engine" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" - "github.com/open-policy-agent/opa/topdown/print" + "github.com/open-policy-agent/opa/v1/topdown/print" "golang.org/x/exp/maps" ) @@ -125,9 +125,9 @@ var builtinFuncNotAllowed = []*ast.Builtin{ // Implements the OPA print.Hook interface to capture and output // print statements from Rego policies during evaluation. -type printHook struct{} +type regoOutputHook struct{} -func (p *printHook) Print(ctx print.Context, msg string) error { +func (p *regoOutputHook) Print(_ print.Context, msg string) error { fmt.Println(msg) return nil } @@ -182,7 +182,7 @@ func (r *Engine) Verify(ctx context.Context, policy *engine.Policy, input []byte if r.enablePrint { options = append(options, rego.EnablePrintStatements(true), - rego.PrintHook(&printHook{}), + rego.PrintHook(®oOutputHook{}), ) } From 7cd97ac3e7a9fb39a2cc6aa9a72039ee0f19b8d8 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Wed, 20 Aug 2025 16:27:29 +0200 Subject: [PATCH 4/6] suppress lint err Signed-off-by: Sylwester Piskozub --- pkg/policies/engine/rego/rego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/policies/engine/rego/rego.go b/pkg/policies/engine/rego/rego.go index c69c44032..763cfb404 100644 --- a/pkg/policies/engine/rego/rego.go +++ b/pkg/policies/engine/rego/rego.go @@ -127,7 +127,7 @@ var builtinFuncNotAllowed = []*ast.Builtin{ // print statements from Rego policies during evaluation. type regoOutputHook struct{} -func (p *regoOutputHook) Print(_ print.Context, msg string) error { +func (p *regoOutputHook) Print(_ print.Context, msg string) error { //nolint:forbidigo fmt.Println(msg) return nil } From 048e145feada5e7ac96f6e9650efa0fa7ac087b1 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Thu, 21 Aug 2025 15:03:27 +0200 Subject: [PATCH 5/6] remove enable print flag, make it default for policy devel eval Signed-off-by: Sylwester Piskozub --- app/cli/cmd/policy_develop_eval.go | 3 --- app/cli/internal/action/policy_develop_eval.go | 2 -- app/cli/internal/policydevel/eval.go | 9 ++++++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/cli/cmd/policy_develop_eval.go b/app/cli/cmd/policy_develop_eval.go index 6ba10ecd2..4aae0d702 100644 --- a/app/cli/cmd/policy_develop_eval.go +++ b/app/cli/cmd/policy_develop_eval.go @@ -33,7 +33,6 @@ func newPolicyDevelopEvalCmd() *cobra.Command { inputs []string allowedHostnames []string debug bool - enablePrint bool ) cmd := &cobra.Command{ @@ -54,7 +53,6 @@ evaluates the policy against the provided material or attestation.`, Inputs: parseKeyValue(inputs), AllowedHostnames: allowedHostnames, Debug: debug, - EnablePrint: enablePrint, } policyEval, err := action.NewPolicyEval(opts, actionOpts) @@ -79,7 +77,6 @@ evaluates the policy against the provided material or attestation.`, cmd.Flags().StringArrayVar(&inputs, "input", []string{}, "Key-value pairs of policy inputs (key=value)") cmd.Flags().StringSliceVar(&allowedHostnames, "allowed-hostnames", []string{}, "Additional hostnames allowed for http.send requests in policies") cmd.Flags().BoolVarP(&debug, "debug", "", false, "Include detailed evaluation inputs/outputs in JSON output and enable verbose logging") - cmd.Flags().BoolVar(&enablePrint, "enable-print", false, "Enable print statements in Rego policies") return cmd } diff --git a/app/cli/internal/action/policy_develop_eval.go b/app/cli/internal/action/policy_develop_eval.go index 6684efb24..708e28446 100644 --- a/app/cli/internal/action/policy_develop_eval.go +++ b/app/cli/internal/action/policy_develop_eval.go @@ -27,7 +27,6 @@ type PolicyEvalOpts struct { Inputs map[string]string AllowedHostnames []string Debug bool - EnablePrint bool } type PolicyEval struct { @@ -51,7 +50,6 @@ func (action *PolicyEval) Run() (*policydevel.EvalSummary, error) { Inputs: action.opts.Inputs, AllowedHostnames: action.opts.AllowedHostnames, Debug: action.opts.Debug, - EnablePrint: action.opts.EnablePrint, } // Evaluate policy diff --git a/app/cli/internal/policydevel/eval.go b/app/cli/internal/policydevel/eval.go index 782a1ac9b..6148cb69c 100644 --- a/app/cli/internal/policydevel/eval.go +++ b/app/cli/internal/policydevel/eval.go @@ -29,6 +29,10 @@ import ( "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials" ) +const ( + enablePrint = true +) + type EvalOptions struct { PolicyPath string MaterialKind string @@ -37,7 +41,6 @@ type EvalOptions struct { Inputs map[string]string AllowedHostnames []string Debug bool - EnablePrint bool } type EvalResult struct { @@ -71,7 +74,7 @@ func Evaluate(opts *EvalOptions, logger zerolog.Logger) (*EvalSummary, error) { material.Annotations = opts.Annotations // 3. Verify material against policy - summary, err := verifyMaterial(schema, material, opts.MaterialPath, opts.Debug, opts.EnablePrint, opts.AllowedHostnames, &logger) + summary, err := verifyMaterial(schema, material, opts.MaterialPath, opts.Debug, opts.AllowedHostnames, &logger) if err != nil { return nil, err } @@ -94,7 +97,7 @@ func createCraftingSchema(policyPath string, inputs map[string]string) (*v1.Craf }, nil } -func verifyMaterial(schema *v1.CraftingSchema, material *v12.Attestation_Material, materialPath string, debug, enablePrint bool, allowedHostnames []string, logger *zerolog.Logger) (*EvalSummary, error) { +func verifyMaterial(schema *v1.CraftingSchema, material *v12.Attestation_Material, materialPath string, debug bool, allowedHostnames []string, logger *zerolog.Logger) (*EvalSummary, error) { var opts []policies.PolicyVerifierOption if len(allowedHostnames) > 0 { opts = append(opts, policies.WithAllowedHostnames(allowedHostnames...)) From c7b4f7d7d45091ac67286ab653446c995f92a2b3 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Thu, 21 Aug 2025 15:06:25 +0200 Subject: [PATCH 6/6] update cli ref Signed-off-by: Sylwester Piskozub --- app/cli/documentation/cli-reference.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/cli/documentation/cli-reference.mdx b/app/cli/documentation/cli-reference.mdx index cc90df65e..90386a335 100755 --- a/app/cli/documentation/cli-reference.mdx +++ b/app/cli/documentation/cli-reference.mdx @@ -2848,7 +2848,6 @@ Options --allowed-hostnames strings Additional hostnames allowed for http.send requests in policies --annotation strings Key-value pairs of material annotations (key=value) --debug Include detailed evaluation inputs/outputs in JSON output and enable verbose logging ---enable-print Enable print statements in Rego policies -h, --help help for eval --input stringArray Key-value pairs of policy inputs (key=value) --kind string Kind of the material: ["ARTIFACT" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CHAINLOOP_RUNNER_CONTEXT" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENVEX" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "TWISTCLI_SCAN_JSON" "ZAP_DAST_ZIP"]