Class: Gitlab::Glaz::GovernPolicyEngine

Inherits:
Object
  • Object
show all
Defined in:
lib/gitlab/glaz/govern_policy_engine.rb

Overview

Wraps a native Glaz::Native::GovernPolicyEngine instance - the Rego governance policy evaluator.

The evaluator is a pure function over its arguments: it performs no I/O, holds no state between calls, and executes no actions. The caller supplies the policy's Rego source and the assembled context document, and is responsible for executing the returned actions.

reasons and actions are independent, not 1:1: reasons reports why the policy matched - one entry per violation/deny element the policy produced; empty means it did not match - while actions reports what to enforce as a result. Actions are conceptually owned by the policy's configuration in the future central Policy Store, so any match currently yields a single synthesized block action regardless of how many reasons fired. matched is a convenience boolean mirroring "+actions+ is non-empty", for callers who don't want to infer that from array emptiness themselves.

#evaluate is the primary path: it always auto-discovers the policy's violation/deny/allow rules (the native evaluate path), returns the hardened Hash shape described below on success, and raises on any evaluation failure - the native call itself raises ArgumentError for bad policy/input and RuntimeError for an engine-side fault; see #evaluate's own doc comment. #debug_evaluate is a separate, secondary method for debugging/testing a Rego query directly: it performs no violation/deny/allow interpretation, does not raise, and returns whatever the query evaluated to (plus any error, in-band) with no guarantee about its shape.

Instance Method Summary collapse

Constructor Details

#initializeGovernPolicyEngine

Returns a new instance of GovernPolicyEngine.



36
37
38
# File 'lib/gitlab/glaz/govern_policy_engine.rb', line 36

def initialize
  @native = ::Glaz::Native::GovernPolicyEngine.new
end

Instance Method Details

#debug_evaluate(policy_rego:, context:, query:, data: {}) ⇒ Hash

Evaluate a Rego query directly, with no violation/deny/allow interpretation. A debugging/testing escape hatch for exercising a policy's Rego directly; prefer #evaluate for the primary path. Unlike #evaluate, this does not raise on failure - error stays in-band, since this method is meant for inspecting raw results (including failures) while developing a policy, not for production decision-making.

Parameters:

  • policy_rego (String)

    Rego policy text (max 64 KiB).

  • context (Hash)

    the host-assembled context, passed to the policy as the Rego input document (max 1 MiB as JSON).

  • query (String)

    Rego query to evaluate (e.g. "data.mypackage.violation"); must not be blank.

  • data (Hash) (defaults to: {})

    optional precomputed Rego data document.

Returns:

  • (Hash)

    { result: Object, error: String | nil } - result is exactly whatever query evaluated to (parsed JSON), with no guarantee about its shape. A non-nil error means evaluation failed; result is then nil.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/gitlab/glaz/govern_policy_engine.rb', line 137

def debug_evaluate(policy_rego:, context:, query:, data: {})
  encoded = ::Glaz::Govern::V1::EvaluateGovernPolicyDebugRequest.encode(
    ::Glaz::Govern::V1::EvaluateGovernPolicyDebugRequest.new(
      policy_rego: policy_rego,
      query: query.to_s,
      input_json: context.to_json,
      data_json: data.nil? || data.empty? ? "" : data.to_json
    )
  )

  response = JSON.parse(@native.debug_evaluate(encoded), symbolize_names: true)

  {
    result: response[:result],
    error: response[:error].nil? || response[:error].empty? ? nil : response[:error]
  }
end

#evaluate(policy_rego:, context:, data: {}) ⇒ Hash

Evaluate a Rego governance policy against context.

Parameters:

  • policy_rego (String)

    Rego policy text (max 64 KiB).

  • context (Hash)

    the host-assembled context, passed to the policy as the Rego input document (max 1 MiB as JSON).

  • data (Hash) (defaults to: {})

    optional precomputed Rego data document (e.g. cached settings or scan results). Top-level keys must not collide with the policy's package path.

Returns:

  • (Hash)

    { matched: Boolean, actions: Array<Hash>, reasons: Array<Hash> }. matched is true exactly when actions (equivalently reasons) is non-empty - a convenience so callers don't have to infer "did the policy fire" from array emptiness. It is not itself an allow/deny decision: today every action is "block", so matched and "should deny" coincide, but a future Policy Store action type (e.g. "log") could match without implying denial - check actions for enforcement decisions. Each action is { action_type: String, message: String | nil, params: Hash }; each reason is { message: String | nil, details: Hash }. Empty actions means the policy allows the operation.

Raises:

  • (ArgumentError)

    for a caller-fixable evaluation failure - malformed policy, oversized documents, an unrecognized violation/deny/allow shape, and so on.

  • (RuntimeError)

    for an engine-side fault, e.g. the Rego evaluation time budget was exceeded - not the caller's fault. Callers can rely on a successful return meaning the policy evaluated cleanly; there is no in-band error to check.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/gitlab/glaz/govern_policy_engine.rb', line 66

def evaluate(policy_rego:, context:, data: {})
  encoded = ::Glaz::Govern::V1::EvaluateGovernPolicyRequest.encode(
    ::Glaz::Govern::V1::EvaluateGovernPolicyRequest.new(
      policy_rego: policy_rego,
      input_json: context.to_json,
      data_json: data.nil? || data.empty? ? "" : data.to_json
    )
  )

  response = ::Glaz::Govern::V1::EvaluateGovernPolicyResponse.decode(
    @native.evaluate(encoded)
  )

  {
    matched: response.matched,
    reasons: response.reasons.map { |reason| reason_hash(reason) },
    actions: response.actions.map { |action| action_hash(action) }
  }
end

#validate(policy_rego:) ⇒ Hash

Validate a Rego policy at save time without evaluating it against input.

Parses and compiles the policy source. A policy that fails to parse is not an error - it is returned as { valid: false, errors: [...] } so the Policy Store can surface actionable feedback without treating a user mistake as an exceptional condition.

Parameters:

  • policy_rego (String)

    Rego policy source text (UTF-8, max 64 KiB). Must be a valid UTF-8 string; raises Encoding::InvalidByteSequenceError if the string contains invalid byte sequences.

Returns:

  • (Hash)

    { valid: Boolean, errors: Array<Hash> }. valid is true when the policy parsed and compiled successfully. errors is empty when valid; each entry is { message: String, location: String } where location is a source-location hint in "LINE:COL" format (e.g. "2:1") when the engine surfaces one, or "" when no separate location is available.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/gitlab/glaz/govern_policy_engine.rb', line 102

def validate(policy_rego:)
  encoded = ::Glaz::Govern::V1::ValidateGovernPolicyRequest.encode(
    ::Glaz::Govern::V1::ValidateGovernPolicyRequest.new(
      policy_rego: policy_rego
    )
  )

  response = ::Glaz::Govern::V1::ValidateGovernPolicyResponse.decode(
    @native.validate(encoded)
  )

  {
    valid: response.valid,
    errors: response.errors.map { |e| { message: e.message, location: e.location } }
  }
end