auth: put mail ingestion on the same rung as resolving a task
IngestMail was AuthRead and SetTaskStatus was AuthWrite, and they answer the same question: may this module change what is on his lists? The old argument for AuthRead — ingestion is additive, it can only produce candidate tasks — is still true and is the weaker half, because a compromised mail reader that can fill the review page indefinitely is not a read. Nothing loses access. AuthWrite outside WriteFact only requires enrollment, which mavmaild already has, and the method does not exist unless the operator wired a mail block. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TrVSBKe3RFDF4fGYKWYQnX
This commit is contained in:
@@ -417,6 +417,28 @@ func TestRequirement_SwapModel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRequirement_ListMutation — the three methods that change what is on his
|
||||||
|
// lists sit on one rung. IngestMail joined them on 2026-08-01; it used to be
|
||||||
|
// AuthRead, which made it disagree with SetTaskStatus about the same question.
|
||||||
|
// CaptureTask stays a read: it puts one line on a list he asked for.
|
||||||
|
func TestRequirement_ListMutation(t *testing.T) {
|
||||||
|
for _, m := range []ipc.Method{
|
||||||
|
ipc.MethodIngestMail, ipc.MethodSetTaskStatus,
|
||||||
|
} {
|
||||||
|
if got := Requirement(m); got != AuthWrite {
|
||||||
|
t.Errorf("%s authority = %v; want AuthWrite", m, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got := Requirement(ipc.MethodCaptureTask); got != AuthRead {
|
||||||
|
t.Errorf("CaptureTask authority = %v; want AuthRead", got)
|
||||||
|
}
|
||||||
|
// mavmaild keeps working: AuthWrite outside WriteFact only needs enrollment.
|
||||||
|
maild := Scope{Surface: SurfaceCoreProcess, Module: "mavmaild", SourceScope: []string{"mail:*"}}
|
||||||
|
if err := Can(ipc.MethodIngestMail, maild, nil); err != nil {
|
||||||
|
t.Errorf("mavmaild ingesting mail = %v; want allowed", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestRequirement_Capture — recording other people is a write, not a read: it
|
// TestRequirement_Capture — recording other people is a write, not a read: it
|
||||||
// puts audio of them on disk. The read side, "что ты записываешь?", is not.
|
// puts audio of them on disk. The read side, "что ты записываешь?", is not.
|
||||||
//
|
//
|
||||||
|
|||||||
+14
-5
@@ -93,6 +93,20 @@ func Requirement(m ipc.Method) Authority {
|
|||||||
return AuthWrite
|
return AuthWrite
|
||||||
case ipc.MethodWriteFact:
|
case ipc.MethodWriteFact:
|
||||||
return AuthWrite
|
return AuthWrite
|
||||||
|
case ipc.MethodIngestMail:
|
||||||
|
// Mail ingestion (Vikunja #246). Moved up from AuthRead on 2026-08-01,
|
||||||
|
// for consistency with SetTaskStatus rather than for a new threat: both
|
||||||
|
// answer the same question — may this module change what is on his
|
||||||
|
// lists? — and they were answering it differently. The old argument was
|
||||||
|
// that ingestion is additive and can only produce candidate tasks, which
|
||||||
|
// is still true; it is the weaker half of the argument, because a
|
||||||
|
// compromised mail reader that can fill the review page indefinitely is
|
||||||
|
// not a read.
|
||||||
|
//
|
||||||
|
// No caller loses anything: AuthWrite outside WriteFact only requires
|
||||||
|
// enrollment, which mavmaild already has, and the method does not exist
|
||||||
|
// unless the operator wired a mail block.
|
||||||
|
return AuthWrite
|
||||||
case ipc.MethodSetTaskStatus:
|
case ipc.MethodSetTaskStatus:
|
||||||
// Resolving a task is NOT additive, which is what separates it from
|
// Resolving a task is NOT additive, which is what separates it from
|
||||||
// capture. Capture at AuthRead can only put a line on a list he reads
|
// capture. Capture at AuthRead can only put a line on a list he reads
|
||||||
@@ -122,11 +136,6 @@ func Requirement(m ipc.Method) Authority {
|
|||||||
// something.
|
// something.
|
||||||
ipc.MethodCaptureTask,
|
ipc.MethodCaptureTask,
|
||||||
ipc.MethodListTasks,
|
ipc.MethodListTasks,
|
||||||
// Mail ingestion (Vikunja #246). AuthRead because of what the method can
|
|
||||||
// produce: candidate tasks and nothing else. It cannot write a fact, set a
|
|
||||||
// reminder, or touch the tool allowlist, so a compromised mail reader can
|
|
||||||
// at worst put junk on a review page he clears in one click.
|
|
||||||
ipc.MethodIngestMail,
|
|
||||||
// Looking at one image (Vikunja #252). AuthRead because of what it can
|
// Looking at one image (Vikunja #252). AuthRead because of what it can
|
||||||
// produce: words about a picture, and optionally a note. It cannot write
|
// produce: words about a picture, and optionally a note. It cannot write
|
||||||
// a fact, set a reminder, or touch the tool allowlist. The invasive part
|
// a fact, set a reminder, or touch the tool allowlist. The invasive part
|
||||||
|
|||||||
Reference in New Issue
Block a user