Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 50 additions & 5 deletions crates/worker/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Worker: pulls tasks from the queue, spawns coven-code sessions, streams progress.

use anyhow::Result;
use anyhow::{Context, Result};
use std::path::Path;
use std::time::Duration;
use tracing::{error, info, warn};
Expand Down Expand Up @@ -738,6 +738,45 @@ fn memory_activity_rows(
rows
}

/// Refuse the entire runtime result when it reports memory activity outside
/// the policy granted in its brief. The result may already have been
/// influenced by a rejected read, so filtering individual entries is not a
/// sufficient publication boundary.
fn enforce_memory_activity(rejections: &[memory::MemoryRejection]) -> Result<()> {
anyhow::ensure!(
rejections.is_empty(),
"runtime reported out-of-policy memory activity; refusing to publish result"
);
Ok(())
Comment on lines +746 to +750
}

#[cfg(test)]
mod memory_enforcement_tests {
use super::*;

#[test]
fn rejected_memory_activity_blocks_the_runtime_result() {
let rejections = vec![memory::MemoryRejection {
op: memory::MemoryOp::Read,
target: "repo/OpenCoven/demo/secrets/api".to_string(),
reason: "memory has been revoked".to_string(),
}];

let error = enforce_memory_activity(&rejections)
.expect_err("a result influenced by revoked memory must not be published");

assert_eq!(
error.to_string(),
"runtime reported out-of-policy memory activity; refusing to publish result"
);
}

#[test]
fn accepted_memory_activity_allows_the_runtime_result() {
enforce_memory_activity(&[]).expect("in-policy memory activity should remain publishable");
}
}

#[allow(clippy::too_many_arguments)]
async fn run_and_publish(
config: &Config,
Expand Down Expand Up @@ -798,10 +837,12 @@ async fn run_and_publish(
store
.revocations_for(task.installation_id, &repo_full)
.await
.unwrap_or_else(|e| {
warn!(task_id = %task.id, "failed to load memory revocations: {e:#}");
Vec::new()
})
.with_context(|| {
format!(
"failed to load memory revocations for installation {}",
task.installation_id
)
})?
Comment on lines +840 to +845
} else {
Vec::new()
};
Expand Down Expand Up @@ -921,6 +962,10 @@ async fn run_and_publish(
if let Err(e) = store.record_memory_activity(activity).await {
warn!(task_id = %task.id, "failed to record memory activity: {e:#}");
}
// A rejected read may already have influenced every field in the
// runtime result. Audit it above, then fail closed before any result
// content can reach a PR, review, status comment, or Check Run.
enforce_memory_activity(&rejections)?;
// Cite the reads the adapter accepted (not refused/revoked) so the
// review discloses which memory influenced it (issue #6).
cited_memory = used
Expand Down
Loading