use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum RuntimeWriteKind {
File,
Directory,
}
impl RuntimeWriteKind {
#[must_use]
pub fn wire_name(self) -> &'static str {
match self {
Self::File => "file",
Self::Directory => "directory",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum RuntimeWriteCondition {
ScriptSession,
PeerFetch,
}
impl RuntimeWriteCondition {
#[must_use]
pub fn wire_name(self) -> &'static str {
match self {
Self::ScriptSession => "script-session",
Self::PeerFetch => "peer-fetch",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct RuntimeWriteReport {
pub path: String,
pub kind: RuntimeWriteKind,
pub condition: RuntimeWriteCondition,
}
struct DeclaredWrite {
entry: &'static str,
kind: RuntimeWriteKind,
condition: RuntimeWriteCondition,
}
impl DeclaredWrite {
fn report(&self) -> RuntimeWriteReport {
RuntimeWriteReport {
path: self.entry.to_string(),
kind: self.kind,
condition: self.condition,
}
}
#[cfg(test)]
fn first_segment(&self) -> &'static str {
self.entry.split('/').next().unwrap_or(self.entry)
}
}
const DECLARED_WRITES: &[DeclaredWrite] = &[
DeclaredWrite {
entry: "peer-in",
kind: RuntimeWriteKind::Directory,
condition: RuntimeWriteCondition::PeerFetch,
},
DeclaredWrite {
entry: "out",
kind: RuntimeWriteKind::Directory,
condition: RuntimeWriteCondition::ScriptSession,
},
];
pub(crate) fn runtime_writes() -> Vec<RuntimeWriteReport> {
DECLARED_WRITES.iter().map(DeclaredWrite::report).collect()
}
#[must_use]
pub fn render_runtime_writes(writes: &[RuntimeWriteReport]) -> String {
let mut out = String::new();
out.push_str(" paths are relative to the work folder\n");
if writes.is_empty() {
out.push_str(" runtime writes: <none>\n");
return out;
}
out.push_str(" runtime writes:\n");
for write in writes {
out.push_str(&format!(
" - {} ({}, {})\n",
write.path,
write.kind.wire_name(),
write.condition.wire_name()
));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
const SCANNED: &[(&str, &str)] = &[
("agent.rs", include_str!("agent.rs")),
("agent/inventory.rs", include_str!("agent/inventory.rs")),
("agent/process.rs", include_str!("agent/process.rs")),
("child_launch.rs", include_str!("child_launch.rs")),
("delegation.rs", include_str!("delegation.rs")),
("delegation_plane.rs", include_str!("delegation_plane.rs")),
("detached.rs", include_str!("detached.rs")),
("hooks.rs", include_str!("hooks.rs")),
("instruction_files.rs", include_str!("instruction_files.rs")),
("manifest_files.rs", include_str!("manifest_files.rs")),
("murmur_md.rs", include_str!("murmur_md.rs")),
("otel.rs", include_str!("otel.rs")),
("network_policy.rs", include_str!("network_policy.rs")),
("plan.rs", include_str!("plan.rs")),
("protected_paths.rs", include_str!("protected_paths.rs")),
("recipes.rs", include_str!("recipes.rs")),
("records_dir.rs", include_str!("records_dir.rs")),
("resource_plane.rs", include_str!("resource_plane.rs")),
("retention.rs", include_str!("retention.rs")),
("runtime.rs", include_str!("runtime.rs")),
(
"runtime/artifact_change.rs",
include_str!("runtime/artifact_change.rs"),
),
("runtime_dir.rs", include_str!("runtime_dir.rs")),
("sandbox.rs", include_str!("sandbox.rs")),
("record_seal.rs", include_str!("record_seal.rs")),
("sealed.rs", include_str!("sealed.rs")),
("shell.rs", include_str!("shell.rs")),
("session_check.rs", include_str!("session_check.rs")),
("tool_annotations.rs", include_str!("tool_annotations.rs")),
("trace.rs", include_str!("trace.rs")),
("trace_blobs.rs", include_str!("trace_blobs.rs")),
("types.rs", include_str!("types.rs")),
("workdir_snapshot.rs", include_str!("workdir_snapshot.rs")),
];
const CONSTANTS: &[(&str, &str)] = &[
("MANIFEST_FILENAME", murmur_artifact::MANIFEST_FILENAME),
("COMPLETION_FILE", crate::delegation::COMPLETION_FILE),
("BLOB_DIR_NAME", crate::trace_blobs::BLOB_DIR_NAME),
("TASK_INPUT_FILE", crate::runtime::TASK_INPUT_FILE),
(
"LEGACY_TASK_INPUT_FILE",
crate::runtime::LEGACY_TASK_INPUT_FILE,
),
("TRACE_FILE_NAME", crate::trace::TRACE_FILE_NAME),
("TRACE_STREAM", crate::record_seal::TRACE_STREAM),
("EVAL_STREAM", crate::record_seal::EVAL_STREAM),
("CHILDREN_DIR", crate::child_launch::CHILDREN_DIR),
("DELEGATIONS_SUBDIR", crate::runtime_dir::DELEGATIONS_SUBDIR),
("HOOKS_SUBDIR", crate::records_dir::HOOKS_SUBDIR),
("HOME_SUBDIR", crate::records_dir::HOME_SUBDIR),
("CHECK_LOG_FILE", crate::session_check::CHECK_LOG_FILE),
("RESULTS_SUBDIR", crate::records_dir::RESULTS_SUBDIR),
("RESULT_FILE", crate::records_dir::RESULT_FILE),
(
"SESSION_TEMP_DIR_NAME",
crate::temp_dir::SESSION_TEMP_DIR_NAME,
),
];
const EXEMPT: &[(&str, &str)] = &[
(
"input.txt",
"Read, never written: the operator's task file `import_task_input` reads once when \
the workdir has no `task.md`. Source text cannot tell a read from a write, so a \
path the runtime only ever opens for reading is exempted here rather than declared \
as something the runtime produces.",
),
(
"package.json",
"Read, never written: the recipe detector opens it to learn which scripts a workdir \
already declares. Exempt on `input.txt`'s terms.",
),
];
const DYNAMIC_EXEMPT: &[(&str, &str, &str)] = &[
(
"record_seal.rs",
"format!(\"{stream_file}{SEAL_SUFFIX}\")",
"A stream's seal, `<stream>.seal` beside the stream it seals: `trace.jsonl.seal`, \
`hooks/eval.jsonl.seal` and `completion.json.seal` through `records_root`, each \
declared in RECORDS_DIR_WRITES, and `conversation.jsonl.seal` in a conversation \
record's own directory under `~/.murmur/conversations`, outside every work folder.",
),
(
"protected_paths.rs",
"&rule.relative",
"A `capabilities.filesystem.read_only` path from the manifest, resolved to decide \
whether a call is refused. The runtime reads it and writes nothing.",
),
(
"protected_paths.rs",
"&lexical",
"The lexically normalised form of a path a tool call named, resolved for the same \
refusal decision.",
),
(
"resource_plane.rs",
"declared_root",
"The `exports.files` / `exports.peer_files` root a manifest declares. The plane \
serves what the capsule put there; it creates nothing of its own.",
),
(
"resource_plane.rs",
"&export.root",
"The same declared root, resolved when a request is served. Exempt on \
`declared_root`'s terms.",
),
(
"runtime.rs",
"&stored_path",
"Where a redeemed peer handle's bytes go, composed by `peer_handoff::stored_path_for` \
under `peer-in/`. Declared through that directory's own entry, which is the name a \
consumer excludes.",
),
(
"runtime.rs",
"rel",
"`write_staged_file`'s path, `tools/<name>/<file>` in the runtime directory, composed \
by each of its callers. Declared through RUNTIME_DIR_WRITES' `tools` entry.",
),
(
"session_check.rs",
"rel.as_ref()",
"A file or directory of an eval case's `start` or `check.files`, copied into the work \
folder by `copy_tree_into`: `start` by `mur eval run` into a freshly staged work \
folder before launch, as an input it stages for the capsule; `check.files` by \
`CheckRunner::run` after the session has ended and before the check's command runs. \
No session writes either.",
),
(
"types.rs",
"rel",
"`StagedSession::write_workdir_file`'s path, named by the embedding caller for an \
input it stages for the capsule before launch. A launch writes nothing there of its \
own.",
),
(
"record_seal.rs",
"rel",
"`InlineChain::open`'s file: a session's `trace.jsonl` through `records_root`, or a \
resumed trace's `trace.jsonl` in the directory `mur run --resume` named it in \
(`ResumeRequest::trace_dir`), the prior session's records directory or the folder \
its trace was copied to, where only `shell_lost` markers are appended.",
),
(
"runtime.rs",
"&name",
"The launched manifest's own file name, copied into the runtime directory through \
`runtime_root`. Declared through RUNTIME_DIR_WRITES' `murmur.yaml` entry, the \
project manifest's name.",
),
(
"records_dir.rs",
"&rel",
"`<workdir root mirrored>/<session-id>` beneath `~/.murmur/records`, created at staging \
outside every workdir.",
),
];
fn production_text(source: &str) -> &str {
match source.find("#[cfg(test)]\nmod tests {") {
Some(index) => &source[..index],
None => source,
}
}
fn parenthesised(source: &str, open: usize) -> &str {
let bytes = source.as_bytes();
let mut depth = 1usize;
let mut index = open;
while index < bytes.len() && depth > 0 {
match bytes[index] {
b'(' => depth += 1,
b')' => depth -= 1,
_ => {}
}
index += 1;
}
assert!(depth == 0, "unbalanced parentheses in a scanned call");
&source[open..index - 1]
}
fn one_line(text: &str) -> String {
text.split_whitespace().collect::<Vec<_>>().join(" ")
}
fn first_argument(arguments: &str) -> &str {
let mut depth = 0i32;
let mut in_string = false;
for (index, character) in arguments.char_indices() {
match character {
'"' => in_string = !in_string,
'(' | '[' | '{' if !in_string => depth += 1,
')' | ']' | '}' if !in_string => depth -= 1,
',' if !in_string && depth == 0 => return &arguments[..index],
_ => {}
}
}
arguments
}
fn workdir_join_arguments(source: &str) -> Vec<String> {
let mut found = Vec::new();
let mut cursor = 0;
while let Some(offset) = source[cursor..].find(".join(") {
let start = cursor + offset;
cursor = start + ".join(".len();
if !source[..start].trim_end().ends_with("workdir") {
continue;
}
found.push(one_line(parenthesised(source, cursor)));
}
found
}
const HELPER_WRITES: &[&str] = &[
".write_file(",
".write_file_exact_mode(",
".overwrite_in_place(",
".open_append(",
".create_dir_all(",
".set_dir_mode(",
];
fn helper_write_arguments(source: &str) -> Vec<String> {
helper_calls(source, HELPER_WRITES)
.into_iter()
.map(|(_, argument)| argument)
.collect()
}
fn helper_calls(source: &str, methods: &[&str]) -> Vec<(String, String)> {
helper_calls_at(source, methods)
.into_iter()
.map(|(_, receiver, argument)| (receiver, argument))
.collect()
}
fn helper_calls_at(source: &str, methods: &[&str]) -> Vec<(usize, String, String)> {
let mut found = Vec::new();
for method in methods {
let mut cursor = 0;
while let Some(offset) = source[cursor..].find(method) {
let start = cursor + offset;
cursor = start + method.len();
if source[..start].ends_with("fs") {
continue;
}
let receiver: String = source[..start]
.trim_end()
.chars()
.rev()
.take_while(|character| character.is_ascii_alphanumeric() || *character == '_')
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect();
found.push((
start,
receiver,
one_line(first_argument(parenthesised(source, cursor))),
));
}
}
found
}
const RUNTIME_DIR_ROOT: &str = "runtime_root";
const RECORDS_DIR_ROOT: &str = "records_root";
const RECORDS_DIR_WRITES: &[(&str, &str)] = &[
(
"trace.jsonl",
"The session's trace: every appender, and a resumed session's `shell_lost` markers.",
),
(
"blobs",
"Trace bodies too large to inline, by their sha256.",
),
(
"trace.jsonl.seal",
"The trace's seal: its chain head signed with the host's record-seal key, written by \
`mur` at `session_end`, at teardown, and by a resumed session's reseal.",
),
(
"hooks/eval.jsonl.seal",
"The scoring hook's `eval.jsonl` sealed by `mur` at teardown: every line's hash, \
signed.",
),
(
"completion.json.seal",
"`completion.json` sealed by `mur` after every write of it.",
),
(
"logs",
"`bootstrap.log`, `otel.log`, one `hook-<name>.log` per failing hook, and a shell \
command's full output: `shell-<ms>.log` for a truncated foreground command and \
`<work-id>.log` for a demoted one, and `check.log`, an eval check's output, written \
after the session ended.",
),
(
"completion.json",
"A delegated child's record of how it ended, written by the child and by its \
parent's watcher.",
),
(
"hooks",
"The `\".\"` of every hook declared `capabilities.filesystem.records: true`.",
),
(
"tmp",
"The session's temporary folder, `/tmp` and `$TMPDIR` to its commands. Created \
empty at staging; the runtime writes nothing in it.",
),
(
"home",
"The session's home folder, `$HOME` to its commands. Created empty at staging; the \
runtime writes nothing in it.",
),
(
"out",
"An agent's `result.txt`, `result_<task-id>.txt` and `compaction-summaries.jsonl`, \
written by the runtime alone.",
),
(
"children",
"One `<capsule>-<16 hex>` work folder per delegated child, the child's `--workdir`.",
),
];
const RECORDS_DIR_DYNAMIC: &[(&str, &str, &str, &str)] = &[
(
"child_launch.rs",
"rel",
"children",
"One delegated child's work folder, `children/<name>-<16 hex>`, composed by \
`child_workdir_rel`.",
),
(
"shell.rs",
"&log.relative",
"logs",
"A foreground shell command's full-output log, `logs/shell-<ms>.log`, composed by \
`detached::output_path_for`.",
),
(
"detached.rs",
"relative",
"logs",
"A demoted shell command's log, `logs/<work-id>.log`, composed by \
`detached::output_path_for` and written as the command runs.",
),
];
fn literal_prefix(argument: &str) -> Option<&str> {
let argument = argument.trim_start_matches('&');
let argument = argument
.strip_prefix("format!(")
.map(str::trim_start)
.unwrap_or(argument);
let (literal, _) = argument.strip_prefix('"')?.split_once('"')?;
Some(literal.split('{').next().unwrap_or(literal))
}
fn names_a_log(argument: &str) -> bool {
literal_prefix(argument)
.is_some_and(|literal| literal == "logs" || literal.starts_with("logs/"))
}
const OUTSIDE_WORKDIR_ROOTS: &[&str] = &["sealed_etc_root", "harness_root", "session_dir_root"];
const RUNTIME_DIR_WRITES: &[(&str, &str)] = &[
(
"tools",
"Every staged and pulled artifact's manifest and `SKILL.md`, and every \
runtime-provided tool's manifest.",
),
(
"MURMUR.md",
"The environment reference, written at staging and after port binding.",
),
(
"task.md",
"The task: `mur run --task`, the import from the workdir, every A2A message and \
every reopen's rewrite.",
),
(
"plans",
"One `plan-<n>.json` per submitted plan, read back when it runs.",
),
(
"murmur.yaml",
"The project manifest, copied on every launch.",
),
(
"delegations",
"One `<delegation-id>.txt` per sub-capsule whose answer was cut in the report its \
parent was handed: the whole answer, written once by `hand_over_answer`.",
),
];
const MOVED_OUT_OF_THE_WORKDIR: &[&str] = &[
"tools",
"MURMUR.md",
"task.md",
"plans",
"murmur.yaml",
".mur-etc",
".process-driver",
"trace.jsonl",
"blobs",
"completion.json",
"eval.jsonl",
];
fn root_of(entry: &str) -> &'static str {
if RECORDS_DIR_WRITES.iter().any(|(name, _)| *name == entry) || entry == "eval.jsonl" {
RECORDS_DIR_ROOT
} else {
RUNTIME_DIR_ROOT
}
}
const MOVED_NAME_WORKDIR_READS: &[(&str, &str, &str)] = &[(
"runtime.rs",
"import_task_input",
"The one read of the operator's `task.md` from the workdir, at session start, copied \
into the runtime directory and never read again.",
)];
const HELPER_READS: &[&str] = &[
".read_file(",
".read_to_string(",
".list_dir(",
".file_len(",
".entry_kind(",
".remove_file(",
".remove_dir_all(",
".open_beneath(",
];
fn resolve_entry_name(argument: &str) -> Option<String> {
let argument = argument.trim_start_matches('&');
let argument = argument
.strip_prefix("format!(")
.map(str::trim_start)
.unwrap_or(argument);
let resolved = if let Some(rest) = argument.strip_prefix('"') {
let (literal, _) = rest.split_once('"')?;
match literal.strip_prefix('{') {
Some(inner) => resolve_constant(argument, inner.split_once('}')?.0)?,
None if literal.starts_with('{') => return None,
None => literal.split('{').next()?.to_string(),
}
} else {
resolve_constant(argument, argument.rsplit("::").next().unwrap_or(argument))?
};
let first = resolved.split('/').next()?.to_string();
(!first.is_empty()).then_some(first)
}
fn resolve_constant(argument: &str, name: &str) -> Option<String> {
let is_constant_name = !name.is_empty()
&& name
.chars()
.all(|character| character.is_ascii_uppercase() || character == '_');
if !is_constant_name {
return None;
}
Some(
CONSTANTS
.iter()
.find(|(constant, _)| *constant == name)
.unwrap_or_else(|| {
panic!(
"a workdir write ({argument}) names the constant {name}, which this \
test's CONSTANTS table does not pair with a value. Add \
(\"{name}\", <path to the constant>) so a rename or a value change is \
caught here rather than silently accepted."
)
})
.1
.to_string(),
)
}
#[test]
fn every_workdir_write_is_declared_or_exempted() {
let declared: Vec<&str> = DECLARED_WRITES
.iter()
.map(DeclaredWrite::first_segment)
.collect();
let mut scanned = 0usize;
let mut dynamic_rows_used = vec![false; DYNAMIC_EXEMPT.len()];
let mut records_dynamic_rows_used = vec![false; RECORDS_DIR_DYNAMIC.len()];
for (file, source) in SCANNED {
let text = production_text(source);
let arguments = workdir_join_arguments(text)
.into_iter()
.map(|argument| (String::new(), argument))
.chain(helper_calls(text, HELPER_WRITES));
for (receiver, argument) in arguments {
scanned += 1;
if OUTSIDE_WORKDIR_ROOTS.contains(&receiver.as_str()) {
continue;
}
let runtime_dir = receiver == RUNTIME_DIR_ROOT;
let records_dir = receiver == RECORDS_DIR_ROOT;
assert!(
records_dir || !names_a_log(&argument),
"{file} writes ({argument}) through `{receiver}`, a workdir root. Every log \
the runtime writes is a record: it goes through the records directory's root \
(`records_root`), which no capsule command can write."
);
if records_dir {
if let Some(row) = RECORDS_DIR_DYNAMIC.iter().position(
|(dynamic_file, dynamic_argument, _, _)| {
dynamic_file == file && *dynamic_argument == argument
},
) {
let entry = RECORDS_DIR_DYNAMIC[row].2;
assert!(
RECORDS_DIR_WRITES.iter().any(|(name, _)| *name == entry),
"RECORDS_DIR_DYNAMIC row ({file:?}, {argument:?}) names `{entry}`, \
which RECORDS_DIR_WRITES does not declare."
);
records_dynamic_rows_used[row] = true;
continue;
}
}
let Some(entry) = resolve_entry_name(&argument) else {
let row =
DYNAMIC_EXEMPT
.iter()
.position(|(exempt_file, exempt_argument, _)| {
exempt_file == file && *exempt_argument == argument
});
assert!(
row.is_some(),
"{file} writes ({argument}) into a workdir, and the argument is neither \
a string literal nor a named constant, so this test cannot say what it \
names. Add ({file:?}, {argument:?}, <written reason>) to this test's \
DYNAMIC_EXEMPT table."
);
dynamic_rows_used[row.unwrap()] = true;
continue;
};
if runtime_dir {
assert!(
RUNTIME_DIR_WRITES.iter().any(|(name, _)| *name == entry),
"{file} writes `{entry}` into the runtime directory, and \
RUNTIME_DIR_WRITES does not declare it. Add (\"{entry}\", <what it is>)."
);
continue;
}
if records_dir {
assert!(
RECORDS_DIR_WRITES.iter().any(|(name, _)| *name == entry),
"{file} writes `{entry}` into the records directory, and \
RECORDS_DIR_WRITES does not declare it. Add (\"{entry}\", <what it is>)."
);
continue;
}
assert!(
!MOVED_OUT_OF_THE_WORKDIR.contains(&entry.as_str()),
"{file} writes `{entry}` through `{receiver}` ({argument}), a workdir root. \
`{entry}` is a file the runtime owns or a record it keeps: it goes through \
`{}`, which no capsule command can write.",
root_of(&entry)
);
assert!(
declared.contains(&entry.as_str())
|| EXEMPT.iter().any(|(name, _)| *name == entry),
"{file} writes `{entry}` into a workdir, and no --explain-scope report \
declares it. Either add an entry for `{entry}` to DECLARED_WRITES in \
workdir_writes.rs, so a consumer diffing the workdir can subtract it, or add \
it to this test's EXEMPT table with a written reason — a path the runtime \
only reads is the usual one."
);
}
}
assert!(
scanned >= 40,
"the workdir write scan found only {scanned} occurrences across {} files, far below \
what the runtime has — the scanner is broken, not the runtime",
SCANNED.len()
);
for ((file, argument, _, _), used) in
RECORDS_DIR_DYNAMIC.iter().zip(records_dynamic_rows_used)
{
assert!(
used,
"RECORDS_DIR_DYNAMIC row ({file:?}, {argument:?}) matches no write through \
`records_root` in {file}; remove it so the table names only what the runtime does"
);
}
for ((file, argument, _), used) in DYNAMIC_EXEMPT.iter().zip(dynamic_rows_used) {
assert!(
used,
"DYNAMIC_EXEMPT row ({file:?}, {argument:?}) matches no write in {file}; remove \
it so the table names only what the runtime does"
);
}
for (name, _) in EXEMPT {
assert!(
!declared.contains(name),
"`{name}` is listed as exempt and is also declared — remove one"
);
}
}
#[test]
fn no_runtime_owned_file_is_reached_through_a_workdir_root() {
let mut checked = 0usize;
let mut rows_used = vec![false; MOVED_NAME_WORKDIR_READS.len()];
for (file, source) in SCANNED {
let text = production_text(source);
let methods: Vec<&str> = HELPER_READS.iter().chain(HELPER_WRITES).copied().collect();
for (start, receiver, argument) in helper_calls_at(text, &methods) {
let Some(entry) = resolve_entry_name(&argument) else {
continue;
};
if !MOVED_OUT_OF_THE_WORKDIR.contains(&entry.as_str()) {
continue;
}
checked += 1;
if receiver == root_of(&entry) {
continue;
}
let function = enclosing_fn(text, start);
let row = MOVED_NAME_WORKDIR_READS
.iter()
.position(|(row_file, row_fn, _)| row_file == file && *row_fn == function);
let Some(row) = row else {
panic!(
"{file}: `{function}` reaches `{entry}` through `{receiver}` ({argument}), \
a workdir root. `{entry}` is a file the runtime owns or a record it \
keeps, and lives outside the workdir: go through `{}`.",
root_of(&entry)
);
};
rows_used[row] = true;
}
}
assert!(
checked >= 20,
"found only {checked} reaches of a runtime-owned file — the scanner is broken"
);
for ((file, function, _), used) in MOVED_NAME_WORKDIR_READS.iter().zip(rows_used) {
assert!(
used,
"MOVED_NAME_WORKDIR_READS row ({file:?}, {function:?}) matches no read; remove it"
);
}
}
#[test]
fn every_source_file_that_joins_a_workdir_is_scanned() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut stack = vec![root.clone()];
let mut checked = 0usize;
while let Some(dir) = stack.pop() {
let entries = std::fs::read_dir(&dir)
.unwrap_or_else(|error| panic!("reading {}: {error}", dir.display()));
for entry in entries {
let path = entry.unwrap().path();
if path.is_dir() {
stack.push(path);
continue;
}
if path.extension().is_some_and(|extension| extension == "rs") {
checked += 1;
if path.file_name().is_some_and(|name| {
name == "workdir_writes.rs"
|| name == "workdir_fs.rs"
|| name == "escape_probe.rs"
}) {
continue;
}
let source = std::fs::read_to_string(&path).unwrap();
let text = production_text(&source);
if workdir_join_arguments(text).is_empty()
&& helper_write_arguments(text).is_empty()
{
continue;
}
let relative = path
.strip_prefix(&root)
.unwrap()
.to_string_lossy()
.replace('\\', "/");
assert!(
SCANNED.iter().any(|(file, _)| *file == relative),
"src/{relative} writes into a workdir but is not in \
every_workdir_write_is_declared_or_exempted's SCANNED list, so nothing \
checks its entries against the declaration. Add \
(\"{relative}\", include_str!(\"{relative}\")) to it."
);
}
}
}
assert!(
checked >= 40,
"walked only {checked} source files under src/ — the walk is broken"
);
}
const RAW_FS_EXEMPT: &[(&str, &str, &str)] = &[
(
"record_seal.rs",
"create_seed",
"Creates `~/.murmur/keys/record-seal.key` under a temporary name opened with \
`O_NOFOLLOW | O_EXCL` and links it into place: the murmur home's key folder, outside \
every work folder and records directory.",
),
(
"record_seal.rs",
"read_seed",
"Opens `~/.murmur/keys/record-seal.key` with `O_NOFOLLOW` and checks its type, owner and \
mode before reading it: outside every work folder.",
),
(
"record_seal.rs",
"load_or_create_uncached",
"Creates `~/.murmur/keys` and writes `record-seal.pub` beside the key: the murmur home, \
outside every work folder.",
),
(
"record_seal.rs",
"load",
"Reads a record-seal public key file the operator named (`mur trace verify \
--public-key`, or `~/.murmur/keys/record-seal.pub`): a key, never a workdir path, \
and only read.",
),
(
"session_check.rs",
"copy_tree_into",
"Reads the files of an eval case's `start` or `check.files`, a directory of the \
operator's outside every work folder, walked by `walk_tree` with no link followed: a \
link or special file in it refuses the copy. What it reads is written into the work \
folder through `WorkdirRoot`.",
),
(
"network_policy.rs",
"resolve_scoped_dir",
"Creates a declared `capabilities.filesystem.scope` beneath the root a WASI guest is \
preopened at. The preopen itself is opened by path by `wasmtime-wasi`, so a root \
here would hold nothing the guest then gets; the scope was validated as relative and \
non-escaping at staging.",
),
(
"resource_plane.rs",
"open_no_follow",
"The resource plane's own reads of files the capsule exported, under its own \
`SymlinkPolicy`; it resolves and confines every path itself and writes nothing.",
),
(
"instruction_files.rs",
"walk_nested",
"Lists the accessible work folder at staging, before any capsule command runs, for \
files named like an `inference.instruction_files` entry. It reads directory entries \
only, follows and enters no link, and writes nothing; the listed files are read \
through `resolve_relpath_beneath_root` and `open_no_follow`, never here.",
),
(
"resource_plane.rs",
"list_export_files",
"The resource plane's listing of a declared export root, on `open_no_follow`'s \
terms.",
),
(
"retention.rs",
"prune_formations",
"A formation directory under `~/.murmur/formations` and its records subtree under `~/.murmur/records`, outside every workdir.",
),
(
"retention.rs",
"prune_records",
"A conversation record directory under `~/.murmur/conversations`, outside every workdir.",
),
(
"retention.rs",
"remove_record",
"A conversation record directory, on `prune_records`'s terms.",
),
(
"retention.rs",
"list_records",
"The conversation record root, on `prune_records`'s terms.",
),
(
"retention.rs",
"child_dirs",
"Directories beneath the conversation record root, on `prune_records`'s terms.",
),
(
"retention.rs",
"locate_message",
"A conversation record file, on `prune_records`'s terms.",
),
(
"retention.rs",
"find_context_dirs_in",
"Context directories beneath the conversation record root, on `prune_records`'s terms.",
),
(
"retention.rs",
"read_context_dir",
"A context directory's record file, on `prune_records`'s terms.",
),
(
"retention.rs",
"count_messages",
"A conversation record file, on `prune_records`'s terms.",
),
(
"retention.rs",
"stage_truncation",
"A conversation record file being truncated, on `prune_records`'s terms.",
),
(
"retention.rs",
"sweep_orphan_blobs",
"Image blobs beside a conversation record being truncated, on `prune_records`'s terms.",
),
(
"retention.rs",
"stage_with_mode",
"`StagedRewrite`, the sibling-file rewrite of a file under `~/.murmur` — a conversation record or a private file — outside every workdir.",
),
(
"retention.rs",
"commit",
"`StagedRewrite`'s rename into place, on `stage_with_mode`'s terms.",
),
(
"retention.rs",
"drop",
"`StagedRewrite`'s removal of an uncommitted sibling file, on `stage_with_mode`'s terms.",
),
(
"runtime.rs",
"check_resume_launchable",
"A conversation record or harness-session file under `~/.murmur/conversations`, \
outside every workdir.",
),
(
"manifest_files.rs",
"read_to_string",
"A prompt file the manifest names, relative to the manifest folder or by absolute \
host path, read before any workdir exists.",
),
(
"manifest_files.rs",
"package_entries",
"`.is_dir()` on an entry of the installed package's zip archive, read from bytes in \
memory; no host path is touched.",
),
(
"manifest_files.rs",
"read_host_skill_source",
"A local skill source the manifest names, relative to the manifest folder or by \
absolute host path, read before any workdir exists.",
),
(
"runtime.rs",
"copy_manifest_into_runtime_dir",
"Reads the project's `murmur.yaml` from the manifest directory; the copy into the \
runtime directory goes through `WorkdirRoot`.",
),
(
"runtime.rs",
"stage_session",
"The operator's `--workdir` itself, checked by path before it is opened as the \
accessible root, and `<manifest dir>/workdir/<session id>`, created before any root \
exists. Nothing beneath either is touched by path.",
),
(
"sandbox.rs",
"test_runtime_dir",
"Test-only: creates a runtime directory's three subdirectories under a temporary \
directory the test owns.",
),
(
"sandbox.rs",
"build_sealed_root",
"Host directories offered as the composed root's base, outside every workdir.",
),
(
"sandbox.rs",
"open_o_path",
"An `O_PATH` handle for a Landlock rule, opened from a host path the grant names; it \
reads and writes nothing.",
),
(
"sandbox.rs",
"open_host_path",
"An `O_PATH` handle on a host path from `~/.murmur/config.yaml`, a directory launch \
refuses when it is or lies under the work folder, opened with no link followed at \
its resolved name; it reads and writes nothing.",
),
(
"sandbox.rs",
"open_host",
"The ELF images of host binaries and libraries a shell grant or a derived interpreter \
runtime names, opened to read their headers and dependencies.",
),
(
"sandbox.rs",
"resolve_soname",
"Host library search directories, outside every workdir.",
),
(
"sealed.rs",
"inspect_installed_profile",
"The host's installed AppArmor profile under `/etc/apparmor.d`.",
),
(
"sealed.rs",
"inspect_profile_attachment",
"`/proc/self/attr/current`, the runtime's own AppArmor attachment.",
),
(
"sealed.rs",
"read_trimmed",
"Host settings under `/sys` and `/proc`.",
),
(
"sealed.rs",
"loaded_apparmor_profiles",
"The names of the kernel's loaded AppArmor profiles under \
`/sys/kernel/security/apparmor`.",
),
];
const RAW_FS_FUNCTIONS: &[&str] = &[
"write(",
"copy(",
"rename(",
"create_dir_all(",
"create_dir(",
"set_permissions(",
"remove_file(",
"remove_dir_all(",
"remove_dir(",
"hard_link(",
"symlink(",
"read_to_string(",
"read(",
];
const RAW_FS_CALLS: &[&str] = &[
"File::create(",
"File::options(",
"OpenOptions::new(",
"File::open(",
"try_exists(",
];
const RAW_FS_PREDICATES: &[&str] = &[".exists()", ".is_file()", ".is_dir()"];
fn enclosing_fn(source: &str, at: usize) -> &str {
let before = &source[..at];
let mut cursor = before.len();
while let Some(index) = before[..cursor].rfind("fn ") {
cursor = index;
let boundary = index == 0
|| !before.as_bytes()[index - 1].is_ascii_alphanumeric()
&& before.as_bytes()[index - 1] != b'_';
let name: String = before[index + 3..]
.chars()
.take_while(|character| character.is_ascii_alphanumeric() || *character == '_')
.collect();
if boundary && !name.is_empty() {
return &before[index + 3..index + 3 + name.len()];
}
}
"<file level>"
}
fn raw_fs_calls(source: &str) -> Vec<(String, String)> {
let mut found = Vec::new();
for function in RAW_FS_FUNCTIONS {
let pattern = format!("fs::{function}");
for (at, _) in source.match_indices(&pattern) {
found.push((enclosing_fn(source, at).to_string(), pattern.clone()));
}
}
for call in RAW_FS_CALLS {
for (at, _) in source.match_indices(call) {
found.push((enclosing_fn(source, at).to_string(), call.to_string()));
}
}
for predicate in RAW_FS_PREDICATES {
for (at, _) in source.match_indices(predicate) {
let receiver = source[..at]
.trim_end()
.rsplit(|character: char| {
!(character.is_ascii_alphanumeric() || "_().".contains(character))
})
.next()
.unwrap_or_default();
let last = receiver
.trim_end_matches("()")
.rsplit('.')
.next()
.unwrap_or_default();
if matches!(last, "meta" | "metadata" | "file_type") {
continue;
}
found.push((enclosing_fn(source, at).to_string(), predicate.to_string()));
}
}
found
}
#[test]
fn no_workdir_file_bypasses_the_no_follow_helper() {
let mut rows_used = vec![false; RAW_FS_EXEMPT.len()];
for (file, source) in SCANNED {
for (function, call) in raw_fs_calls(production_text(source)) {
let row = RAW_FS_EXEMPT
.iter()
.position(|(exempt_file, exempt_fn, _)| {
exempt_file == file && *exempt_fn == function
});
let Some(row) = row else {
panic!(
"{file}: `{function}` calls `{call}` directly. A path beneath the \
accessible workdir or the session directory goes through WorkdirRoot \
(crate::workdir_fs), which follows no link; a path outside every \
workdir gets a ({file:?}, {function:?}, <why it is not beneath a \
workdir>) row in RAW_FS_EXEMPT."
);
};
rows_used[row] = true;
}
}
for ((file, function, _), used) in RAW_FS_EXEMPT.iter().zip(rows_used) {
assert!(
used,
"RAW_FS_EXEMPT row ({file:?}, {function:?}) matches no raw filesystem call — \
the call is gone or moved; remove or re-key the row"
);
}
}
#[test]
fn the_raw_call_scanner_names_the_function_and_the_call() {
let source = "fn staged() {\n let _ = std::fs::write(self.workdir.join(\"plans\").join(\"x\"), b\"\");\n}\nfn helper(root: &WorkdirRoot) {\n root.write_file(\"plans/x\", b\"\", 0o644);\n if metadata.is_file() {}\n}\n";
assert_eq!(
raw_fs_calls(source),
vec![("staged".to_string(), "fs::write(".to_string())]
);
assert_eq!(
helper_write_arguments(source),
vec!["\"plans/x\"".to_string()]
);
assert_eq!(resolve_entry_name("\"plans/x\"").as_deref(), Some("plans"));
assert_eq!(
resolve_entry_name("format!(\"{BLOB_DIR_NAME}/{sha256}\")").as_deref(),
Some("blobs")
);
assert_eq!(
resolve_entry_name("format!(\"tools/{name}/x\")").as_deref(),
Some("tools")
);
assert_eq!(
resolve_entry_name("CHILDREN_DIR").as_deref(),
Some("children")
);
assert_eq!(resolve_entry_name("&rel"), None);
assert_eq!(resolve_entry_name("format!(\"{}/x\", a)"), None);
}
#[test]
fn runtime_writes_lists_none_of_the_runtime_directory_files() {
for write in runtime_writes() {
let last = write.path.rsplit('/').next().unwrap_or_default();
assert!(
!MOVED_OUT_OF_THE_WORKDIR.contains(&last),
"{} is declared as a workdir write",
write.path
);
}
}
#[test]
fn no_declared_path_is_a_shell_log() {
for write in runtime_writes() {
assert!(
write.path != "logs" && !write.path.starts_with("logs/"),
"{} is declared as a workdir write",
write.path
);
}
}
#[test]
fn runtime_writes_are_exactly_peer_in_and_a_script_capsules_out() {
assert_eq!(
runtime_writes(),
vec![
RuntimeWriteReport {
path: "peer-in".to_string(),
kind: RuntimeWriteKind::Directory,
condition: RuntimeWriteCondition::PeerFetch,
},
RuntimeWriteReport {
path: "out".to_string(),
kind: RuntimeWriteKind::Directory,
condition: RuntimeWriteCondition::ScriptSession,
},
]
);
for write in runtime_writes() {
for old in [".murmur", ".capsule-home", "children", "home"] {
assert!(!write.path.contains(old), "{}", write.path);
}
}
}
#[test]
fn no_declared_path_is_a_temp_store() {
for write in runtime_writes() {
let last = write.path.rsplit('/').next().unwrap_or_default();
assert!(
!last.contains("tmp"),
"{} is declared as a workdir write",
write.path
);
}
}
#[test]
fn a_serialized_row_carries_exactly_path_kind_and_condition() {
for write in runtime_writes() {
let line = serde_json::to_string(&write).unwrap();
assert_eq!(
line,
format!(
r#"{{"path":"{}","kind":"{}","condition":"{}"}}"#,
write.path,
write.kind.wire_name(),
write.condition.wire_name()
)
);
}
}
#[test]
fn no_declared_path_is_reported_twice() {
let writes = runtime_writes();
let mut paths: Vec<&str> = writes.iter().map(|write| write.path.as_str()).collect();
paths.sort_unstable();
let count = paths.len();
paths.dedup();
assert_eq!(paths.len(), count, "a path is declared twice");
}
#[test]
fn the_rendering_names_every_declared_path_relative_to_the_work_folder() {
let writes = runtime_writes();
let rendered = render_runtime_writes(&writes);
assert!(rendered.contains("paths are relative to the work folder"));
for write in &writes {
assert!(
rendered.contains(&write.path),
"{} is missing from the rendering",
write.path
);
}
assert!(rendered.ends_with('\n'));
}
}