-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Project
term-challenge
Description
The /api/v1/validator/log_task endpoint accepts a score: f64 field from validators without any validation. A malicious validator can submit arbitrary score values (including negative, extreme, or NaN values) to manipulate agent data.
Severity: HIGH
Location:
src/api/handlers.rs(lines 1704, 1832)src/api/routes/validator.rs(line 366)- Endpoint:
POST /api/v1/validator/log_task
Error Message
Debug Logs
System Information
Bounty Version: 0.1.0
OS: Ubuntu 24.04 LTS
CPU: AMD EPYC-Genoa Processor (8 cores)
RAM: 15 GBScreenshots
No response
Steps to Reproduce
- Set up a validator with valid credentials (hotkey, signature)
- Send a POST request to
/api/v1/validator/log_taskwith manipulated score values:
Attack Vector 1 - Score/Passed Mismatch:
curl -X POST /api/v1/validator/log_task \
-H "Content-Type: application/json" \
-d '{
"validator_hotkey": "<valid_hotkey>",
"signature": "<valid_signature>",
"timestamp": 1737400000,
"agent_hash": "abc123...",
"task_id": "task-001",
"task_name": "task-001",
"passed": false,
"score": 1.0,
"execution_time_ms": 1000,
"steps": 5,
"cost_usd": 0.01,
"started_at": 1737399900
}'Attack Vector 2 - Extreme Values:
{ "passed": true, "score": 999999.0 }Attack Vector 3 - Negative Scores:
{ "passed": false, "score": -100.0 }Attack Vector 4 - Special Float Values:
{ "passed": true, "score": "NaN" }Expected Behavior
The server should validate the score field:
- Reject NaN and Infinity values
- Enforce score range between 0.0 and 1.0
- Ensure score matches the
passedboolean (1.0 for passed, 0.0 for failed)
The legitimate validator code (src/worker/validator.rs:2090) correctly computes score:
"score": if passed { 1.0 } else { 0.0 },The server should enforce the same rule.
Actual Behavior
The server accepts any score value without validation and stores it directly in the database:
// src/api/handlers.rs:1824-1847
let task_log = TaskLog {
// ...
passed: req.passed,
score: req.score, // Directly stored without validation!
// ...
};The LogTaskRequest struct accepts any f64 value:
// src/api/handlers.rs:1696-1720
pub struct LogTaskRequest {
// ...
pub passed: bool,
pub score: f64, // NO VALIDATION - accepts any value
// ...
}Additional Context
Mitigating Factor: The final consensus score is calculated from passed_tasks/total_tasks ratio (using the passed boolean), not from summing the score field. However, the total_score field IS stored in TaskLogSummary and used in database queries:
-- src/storage/pg.rs:4157
COALESCE(SUM(score::FLOAT8), 0.0)::FLOAT8Suggested Fix:
// Add to log_task handler before creating TaskLog
// Validate score bounds
if req.score.is_nan() || req.score.is_infinite() {
return Err((StatusCode::BAD_REQUEST, Json(LogTaskResponse {
success: false,
error: Some("Invalid score value (NaN or Infinite)".to_string()),
..Default::default()
})));
}
if req.score < 0.0 || req.score > 1.0 {
return Err((StatusCode::BAD_REQUEST, Json(LogTaskResponse {
success: false,
error: Some("Score must be between 0.0 and 1.0".to_string()),
..Default::default()
})));
}
// Better: Compute score server-side from passed field
let score = if req.passed { 1.0 } else { 0.0 };Additional Recommendations:
- Server should compute the score from the
passedfield rather than trusting the client - Add database constraints:
CHECK (score >= 0.0 AND score <= 1.0) - Log anomalous scores for monitoring