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 =
111 config_->test_requirements.size() + config_->change_constraints.size() +
112 config_->forbidden_ranges.size() + config_->review_requirements.size();
113
114 return absl::StrFormat("Policies enabled (%d policies loaded from %s)",
115 total_policies, policy_path_);
116}
117
118absl::Status PolicyEvaluator::ParsePolicyFile(absl::string_view yaml_content) {
119 // For now, implement a simple key-value parser
120 // In production, we'd use yaml-cpp or similar library
121 // This stub implementation allows the system to work without YAML dependency
122
123 config_ = std::make_unique<PolicyConfig>();
124 config_->version = "1.0";
125 config_->enabled = true;
126
127 // Parse simple YAML-like format
128 std::vector<std::string> lines = absl::StrSplit(yaml_content, '\n');
129 bool in_policies = false;
130 std::string current_policy_type;
131 std::string current_policy_name;
132
133 for (const auto& line : lines) {
134 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
135
136 // Skip comments and empty lines
137 if (trimmed.empty() || trimmed[0] == '#')
138 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(std::make_tuple(0xFFB0, 0xFFFF, "ROM header"));
177 range.message = "Cannot modify protected region";
178 config_->forbidden_ranges.push_back(range);
179 } else if (current_policy_type == "test_requirement") {
181 test.name = current_policy_name;
182 test.test_suites.push_back(std::make_pair("smoke_test", 1.0));
183 test.message = "Required tests must pass";
184 config_->test_requirements.push_back(test);
185 } else if (current_policy_type == "review_requirement") {
187 review.name = current_policy_name;
188 review.message = "Manual review required";
189 config_->review_requirements.push_back(review);
190 }
191 }
192 }
193 }
194
195 if (!config_->enabled) {
196 enabled_ = false;
197 return absl::OkStatus();
198 }
199
200 enabled_ = true;
201 return absl::OkStatus();
202}
203
204absl::StatusOr<PolicyResult> PolicyEvaluator::EvaluateProposal(
205 absl::string_view proposal_id) {
206 PolicyResult result;
207 result.passed = true;
208
209 if (!enabled_ || !config_) {
210 // No policies - everything passes
211 return result;
212 }
213
214 // Evaluate each policy type
215 EvaluateTestRequirements(std::string(proposal_id), &result);
216 EvaluateChangeConstraints(std::string(proposal_id), &result);
217 EvaluateForbiddenRanges(std::string(proposal_id), &result);
218 EvaluateReviewRequirements(std::string(proposal_id), &result);
219
220 // Categorize violations by severity
221 CategorizeViolations(&result);
222
223 // Determine overall pass/fail
224 result.passed = !result.has_critical_violations();
225
226 return result;
227}
228
229void PolicyEvaluator::EvaluateTestRequirements(absl::string_view proposal_id,
230 PolicyResult* result) {
231 // TODO: Implement test requirement evaluation
232 // For now, all test requirements pass (no test framework yet)
233 std::string proposal_id_str(proposal_id);
234 for (const auto& policy : config_->test_requirements) {
235 if (!policy.enabled)
236 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)
256 continue;
257
258 // Check max bytes changed
259 if (policy.max_bytes_changed > 0 &&
260 proposal.bytes_changed > policy.max_bytes_changed) {
261 PolicyViolation violation;
262 violation.policy_name = policy.name;
263 violation.severity = policy.severity;
264 violation.message =
265 absl::StrFormat("%s: %d bytes changed (limit: %d)", policy.message,
266 proposal.bytes_changed, policy.max_bytes_changed);
267 violation.details =
268 absl::StrFormat("Proposal changed %d bytes", proposal.bytes_changed);
269 result->violations.push_back(violation);
270 }
271
272 // Check max commands executed
273 if (policy.max_commands_executed > 0 &&
274 proposal.commands_executed > policy.max_commands_executed) {
275 PolicyViolation violation;
276 violation.policy_name = policy.name;
277 violation.severity = policy.severity;
278 violation.message = absl::StrFormat(
279 "%s: %d commands executed (limit: %d)", policy.message,
280 proposal.commands_executed, policy.max_commands_executed);
281 violation.details = absl::StrFormat("Proposal executed %d commands",
282 proposal.commands_executed);
283 result->violations.push_back(violation);
284 }
285 }
286}
287
288void PolicyEvaluator::EvaluateForbiddenRanges(absl::string_view proposal_id,
289 PolicyResult* result) {
290 // TODO: Implement forbidden range checking
291 // Would need to parse diff or track ROM modifications
292 // For now, we assume no forbidden range violations
293 for (const auto& policy : config_->forbidden_ranges) {
294 if (!policy.enabled)
295 continue;
296
297 // Placeholder: would check ROM modification ranges here
298 }
299}
300
301void PolicyEvaluator::EvaluateReviewRequirements(absl::string_view proposal_id,
302 PolicyResult* result) {
303 auto& registry = ProposalRegistry::Instance();
304 auto proposal_result = registry.GetProposal(std::string(proposal_id));
305
306 if (!proposal_result.ok()) {
307 return;
308 }
309
310 const auto& proposal = proposal_result.value();
311
312 for (const auto& policy : config_->review_requirements) {
313 if (!policy.enabled)
314 continue;
315
316 // Evaluate conditions
317 for (const auto& condition : policy.conditions) {
318 bool condition_met = false;
319
320 // Simple condition evaluation
321 if (absl::StrContains(condition.if_clause, "bytes_changed")) {
322 // Extract threshold from condition like "bytes_changed > 1024"
323 if (absl::StrContains(condition.if_clause, ">")) {
324 std::vector<std::string> parts =
325 absl::StrSplit(condition.if_clause, '>');
326 if (parts.size() == 2) {
327 int threshold;
328 if (absl::SimpleAtoi(absl::StripAsciiWhitespace(parts[1]),
329 &threshold)) {
330 condition_met = (proposal.bytes_changed > threshold);
331 }
332 }
333 }
334 } else if (absl::StrContains(condition.if_clause, "commands_executed")) {
335 if (absl::StrContains(condition.if_clause, ">")) {
336 std::vector<std::string> parts =
337 absl::StrSplit(condition.if_clause, '>');
338 if (parts.size() == 2) {
339 int threshold;
340 if (absl::SimpleAtoi(absl::StripAsciiWhitespace(parts[1]),
341 &threshold)) {
342 condition_met = (proposal.commands_executed > threshold);
343 }
344 }
345 }
346 }
347
348 if (condition_met) {
349 PolicyViolation violation;
350 violation.policy_name = policy.name;
351 violation.severity = policy.severity;
352 violation.message =
353 condition.message.empty() ? policy.message : condition.message;
354 violation.details =
355 absl::StrFormat("Condition met: %s → %s", condition.if_clause,
356 condition.then_clause);
357 result->violations.push_back(violation);
358 }
359 }
360 }
361}
362
364 for (const auto& violation : result->violations) {
365 switch (violation.severity) {
367 result->critical_violations.push_back(violation);
368 break;
370 result->warnings.push_back(violation);
371 break;
373 result->info.push_back(violation);
374 break;
375 }
376 }
377}
378
379} // namespace cli
380} // 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()
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