yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
policy_evaluator.cc
Go to the documentation of this file.
2
3#include <fstream>
4#include <sstream>
5
6#include "absl/strings/numbers.h"
7#include "absl/strings/str_format.h"
8#include "absl/strings/str_split.h"
10
11namespace yaze {
12namespace cli {
13
14// Internal policy configuration structures
16 std::string version;
17 bool enabled = true;
18
20 std::string name;
21 bool enabled = true;
23 // suite name → min pass rate
24 std::vector<std::pair<std::string, double>> test_suites;
25 std::string message;
26 };
27
39
41 std::string name;
42 bool enabled = true;
44 // start, end, reason
45 std::vector<std::tuple<int, int, std::string>> ranges;
46 std::string message;
47 };
48
50 std::string name;
51 bool enabled = true;
53 struct Condition {
54 std::string if_clause; // e.g., "bytes_changed > 1024"
55 std::string then_clause; // e.g., "require_diff_review"
56 std::string message;
57 };
58 std::vector<Condition> conditions;
59 std::string message;
60 };
61
62 std::vector<TestRequirement> test_requirements;
63 std::vector<ChangeConstraint> change_constraints;
64 std::vector<ForbiddenRange> forbidden_ranges;
65 std::vector<ReviewRequirement> review_requirements;
66};
67
68// Singleton instance
70 static PolicyEvaluator instance;
71 return instance;
72}
73
74absl::Status PolicyEvaluator::LoadPolicies(absl::string_view policy_dir) {
75 policy_dir_ = std::string(policy_dir);
76 policy_path_ = absl::StrFormat("%s/agent.yaml", policy_dir);
77
78 // Check if file exists
79 std::ifstream file(policy_path_);
80 if (!file.good()) {
81 // No policy file - policies disabled
82 enabled_ = false;
83 return absl::OkStatus();
84 }
85
86 // Read file content
87 std::stringstream buffer;
88 buffer << file.rdbuf();
89 std::string yaml_content = buffer.str();
90
91 return ParsePolicyFile(yaml_content);
92}
93
95 if (policy_dir_.empty()) {
96 return absl::FailedPreconditionError(
97 "No policy directory set. Call LoadPolicies first.");
98 }
100}
101
103 if (!enabled_) {
104 return "Policies disabled (no configuration file)";
105 }
106 if (!config_) {
107 return "Policies enabled but not loaded";
108 }
109
110 int total_policies = config_->test_requirements.size() +
111 config_->change_constraints.size() +
112 config_->forbidden_ranges.size() +
113 config_->review_requirements.size();
114
115 return absl::StrFormat("Policies enabled (%d policies loaded from %s)",
116 total_policies, policy_path_);
117}
118
119absl::Status PolicyEvaluator::ParsePolicyFile(absl::string_view yaml_content) {
120 // For now, implement a simple key-value parser
121 // In production, we'd use yaml-cpp or similar library
122 // This stub implementation allows the system to work without YAML dependency
123
124 config_ = std::make_unique<PolicyConfig>();
125 config_->version = "1.0";
126 config_->enabled = true;
127
128 // Parse simple YAML-like format
129 std::vector<std::string> lines = absl::StrSplit(yaml_content, '\n');
130 bool in_policies = false;
131 std::string current_policy_type;
132 std::string current_policy_name;
133
134 for (const auto& line : lines) {
135 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
136
137 // Skip comments and empty lines
138 if (trimmed.empty() || trimmed[0] == '#') continue;
139
140 // Check for main keys
141 if (absl::StartsWith(trimmed, "version:")) {
142 std::vector<std::string> parts = absl::StrSplit(trimmed, ':');
143 if (parts.size() >= 2) {
144 config_->version = std::string(absl::StripAsciiWhitespace(parts[1]));
145 }
146 } else if (absl::StartsWith(trimmed, "enabled:")) {
147 std::vector<std::string> parts = absl::StrSplit(trimmed, ':');
148 if (parts.size() >= 2) {
149 std::string value = std::string(absl::StripAsciiWhitespace(parts[1]));
150 config_->enabled = (value == "true");
151 }
152 } else if (trimmed == "policies:") {
153 in_policies = true;
154 } else if (in_policies && absl::StartsWith(trimmed, "- name:")) {
155 // Start of new policy
156 std::vector<std::string> parts = absl::StrSplit(trimmed, ':');
157 if (parts.size() >= 2) {
158 current_policy_name = std::string(absl::StripAsciiWhitespace(parts[1]));
159 }
160 } else if (in_policies && absl::StartsWith(trimmed, "type:")) {
161 std::vector<std::string> parts = absl::StrSplit(trimmed, ':');
162 if (parts.size() >= 2) {
163 current_policy_type = std::string(absl::StripAsciiWhitespace(parts[1]));
164
165 // Create appropriate policy structure
166 if (current_policy_type == "change_constraint") {
168 constraint.name = current_policy_name;
169 constraint.max_bytes_changed = 5120; // Default 5KB
170 constraint.max_commands_executed = 15;
171 constraint.message = "Change scope exceeded";
172 config_->change_constraints.push_back(constraint);
173 } else if (current_policy_type == "forbidden_range") {
175 range.name = current_policy_name;
176 range.ranges.push_back(
177 std::make_tuple(0xFFB0, 0xFFFF, "ROM header"));
178 range.message = "Cannot modify protected region";
179 config_->forbidden_ranges.push_back(range);
180 } else if (current_policy_type == "test_requirement") {
182 test.name = current_policy_name;
183 test.test_suites.push_back(std::make_pair("smoke_test", 1.0));
184 test.message = "Required tests must pass";
185 config_->test_requirements.push_back(test);
186 } else if (current_policy_type == "review_requirement") {
188 review.name = current_policy_name;
189 review.message = "Manual review required";
190 config_->review_requirements.push_back(review);
191 }
192 }
193 }
194 }
195
196 if (!config_->enabled) {
197 enabled_ = false;
198 return absl::OkStatus();
199 }
200
201 enabled_ = true;
202 return absl::OkStatus();
203}
204
205absl::StatusOr<PolicyResult> PolicyEvaluator::EvaluateProposal(
206 absl::string_view proposal_id) {
207 PolicyResult result;
208 result.passed = true;
209
210 if (!enabled_ || !config_) {
211 // No policies - everything passes
212 return result;
213 }
214
215 // Evaluate each policy type
216 EvaluateTestRequirements(std::string(proposal_id), &result);
217 EvaluateChangeConstraints(std::string(proposal_id), &result);
218 EvaluateForbiddenRanges(std::string(proposal_id), &result);
219 EvaluateReviewRequirements(std::string(proposal_id), &result);
220
221 // Categorize violations by severity
222 CategorizeViolations(&result);
223
224 // Determine overall pass/fail
225 result.passed = !result.has_critical_violations();
226
227 return result;
228}
229
230void PolicyEvaluator::EvaluateTestRequirements(absl::string_view proposal_id,
231 PolicyResult* result) {
232 // TODO: Implement test requirement evaluation
233 // For now, all test requirements pass (no test framework yet)
234 std::string proposal_id_str(proposal_id);
235 for (const auto& policy : config_->test_requirements) {
236 if (!policy.enabled) continue;
237
238 // Placeholder: would check actual test results here
239 // For now, we skip test validation
240 }
241}
242
243void PolicyEvaluator::EvaluateChangeConstraints(absl::string_view proposal_id,
244 PolicyResult* result) {
245 auto& registry = ProposalRegistry::Instance();
246 auto proposal_result = registry.GetProposal(std::string(proposal_id));
247
248 if (!proposal_result.ok()) {
249 return; // Can't evaluate non-existent proposal
250 }
251
252 const auto& proposal = proposal_result.value();
253
254 for (const auto& policy : config_->change_constraints) {
255 if (!policy.enabled) continue;
256
257 // Check max bytes changed
258 if (policy.max_bytes_changed > 0 &&
259 proposal.bytes_changed > policy.max_bytes_changed) {
260 PolicyViolation violation;
261 violation.policy_name = policy.name;
262 violation.severity = policy.severity;
263 violation.message = absl::StrFormat(
264 "%s: %d bytes changed (limit: %d)", policy.message,
265 proposal.bytes_changed, policy.max_bytes_changed);
266 violation.details = absl::StrFormat("Proposal changed %d bytes",
267 proposal.bytes_changed);
268 result->violations.push_back(violation);
269 }
270
271 // Check max commands executed
272 if (policy.max_commands_executed > 0 &&
273 proposal.commands_executed > policy.max_commands_executed) {
274 PolicyViolation violation;
275 violation.policy_name = policy.name;
276 violation.severity = policy.severity;
277 violation.message = absl::StrFormat(
278 "%s: %d commands executed (limit: %d)", policy.message,
279 proposal.commands_executed, policy.max_commands_executed);
280 violation.details = absl::StrFormat("Proposal executed %d commands",
281 proposal.commands_executed);
282 result->violations.push_back(violation);
283 }
284 }
285}
286
287void PolicyEvaluator::EvaluateForbiddenRanges(absl::string_view proposal_id,
288 PolicyResult* result) {
289 // TODO: Implement forbidden range checking
290 // Would need to parse diff or track ROM modifications
291 // For now, we assume no forbidden range violations
292 for (const auto& policy : config_->forbidden_ranges) {
293 if (!policy.enabled) continue;
294
295 // Placeholder: would check ROM modification ranges here
296 }
297}
298
299void PolicyEvaluator::EvaluateReviewRequirements(absl::string_view proposal_id,
300 PolicyResult* result) {
301 auto& registry = ProposalRegistry::Instance();
302 auto proposal_result = registry.GetProposal(std::string(proposal_id));
303
304 if (!proposal_result.ok()) {
305 return;
306 }
307
308 const auto& proposal = proposal_result.value();
309
310 for (const auto& policy : config_->review_requirements) {
311 if (!policy.enabled) continue;
312
313 // Evaluate conditions
314 for (const auto& condition : policy.conditions) {
315 bool condition_met = false;
316
317 // Simple condition evaluation
318 if (absl::StrContains(condition.if_clause, "bytes_changed")) {
319 // Extract threshold from condition like "bytes_changed > 1024"
320 if (absl::StrContains(condition.if_clause, ">")) {
321 std::vector<std::string> parts =
322 absl::StrSplit(condition.if_clause, '>');
323 if (parts.size() == 2) {
324 int threshold;
325 if (absl::SimpleAtoi(absl::StripAsciiWhitespace(parts[1]),
326 &threshold)) {
327 condition_met = (proposal.bytes_changed > threshold);
328 }
329 }
330 }
331 } else if (absl::StrContains(condition.if_clause, "commands_executed")) {
332 if (absl::StrContains(condition.if_clause, ">")) {
333 std::vector<std::string> parts =
334 absl::StrSplit(condition.if_clause, '>');
335 if (parts.size() == 2) {
336 int threshold;
337 if (absl::SimpleAtoi(absl::StripAsciiWhitespace(parts[1]),
338 &threshold)) {
339 condition_met = (proposal.commands_executed > threshold);
340 }
341 }
342 }
343 }
344
345 if (condition_met) {
346 PolicyViolation violation;
347 violation.policy_name = policy.name;
348 violation.severity = policy.severity;
349 violation.message =
350 condition.message.empty() ? policy.message : condition.message;
351 violation.details = absl::StrFormat(
352 "Condition met: %s → %s", condition.if_clause, condition.then_clause);
353 result->violations.push_back(violation);
354 }
355 }
356 }
357}
358
360 for (const auto& violation : result->violations) {
361 switch (violation.severity) {
363 result->critical_violations.push_back(violation);
364 break;
366 result->warnings.push_back(violation);
367 break;
369 result->info.push_back(violation);
370 break;
371 }
372 }
373}
374
375} // namespace cli
376} // namespace yaze
absl::Status ParsePolicyFile(absl::string_view yaml_content)
void EvaluateReviewRequirements(absl::string_view proposal_id, PolicyResult *result)
absl::StatusOr< PolicyResult > EvaluateProposal(absl::string_view proposal_id)
std::unique_ptr< PolicyConfig > config_
static PolicyEvaluator & GetInstance()
void EvaluateTestRequirements(absl::string_view proposal_id, PolicyResult *result)
void CategorizeViolations(PolicyResult *result)
std::string GetStatusString() const
void EvaluateForbiddenRanges(absl::string_view proposal_id, PolicyResult *result)
absl::Status LoadPolicies(absl::string_view policy_dir=".yaze/policies")
void EvaluateChangeConstraints(absl::string_view proposal_id, PolicyResult *result)
static ProposalRegistry & Instance()
Main namespace for the application.
std::vector< std::tuple< int, int, std::string > > ranges
std::vector< std::pair< std::string, double > > test_suites
std::vector< TestRequirement > test_requirements
std::vector< ForbiddenRange > forbidden_ranges
std::vector< ReviewRequirement > review_requirements
std::vector< ChangeConstraint > change_constraints
std::vector< PolicyViolation > warnings
bool has_critical_violations() const
std::vector< PolicyViolation > info
std::vector< PolicyViolation > critical_violations
std::vector< PolicyViolation > violations