MAS-WP-0011 finished: peer authentication in mode contract v0.1
CONTRACT.md section 7 (founder-reviewed): pairwise HMAC-SHA256 over TIMESTAMP.BODY in MAS-Modes-* headers, 300 s window; master commands only from the configured master; statuses only at the handoff's tokenized URL from its peer; primed webhooks must name a known peer. mas_modes.signing, signed contract.post, authenticating json_server, EmailMode/stubs/gateway signing. conformance/auth.json vectors + 7 authorization flows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 5787@bnt-lap001 Assistant-Session: 5db26b82-138b-40cc-9856-4d5eb0f752fd
This commit is contained in:
parent
2bc41a3318
commit
bdf200a600
25 changed files with 1603 additions and 151 deletions
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"description": "Scripted flows (CONTRACT.md section 7). Each runs against a fresh email side with the callback stub and, for 'slave', a stub outside master that assigns EXT-100, EXT-101, ... The statute is statute.json. Placeholders: {case_id}, {message_ref}, {handoff_id} come from the latest response that carried them; {callback} is the stub's handoff URL. Master role config: ticket, modes {callback}, fields phone/time, timeout 3600 s. Slave role config: case, defaults autonomous_send false, transfers block, overridden by 'config'.",
|
||||
"description": "Scripted flows (CONTRACT.md section 7). Each runs against a fresh email side with the callback stub and, for 'slave', a stub outside master that assigns EXT-100, EXT-101, ... The statute is statute.json. Placeholders: {case_id}, {message_ref}, {handoff_id} come from the latest response that carried them; {callback} is the stub's handoff URL. Peers: email, callback-1 (the stub), cms-1 (the stub master); every request is signed (CONTRACT.md section 7). Master role config: ticket, modes {callback}, fields phone/time, timeout 3600 s. Slave role config: case, defaults autonomous_send false, transfers block, overridden by 'config'.",
|
||||
"flows": [
|
||||
{
|
||||
"id": "m-001",
|
||||
|
|
@ -340,7 +340,8 @@
|
|||
"title": "transfer to the primed webhook; statuses relayed",
|
||||
"config": {
|
||||
"transfers": {
|
||||
"webhook": "{callback}"
|
||||
"webhook": "{callback}",
|
||||
"peer": "callback-1"
|
||||
}
|
||||
},
|
||||
"scenario": "completed",
|
||||
|
|
@ -382,7 +383,8 @@
|
|||
"title": "the master owns the timeout",
|
||||
"config": {
|
||||
"transfers": {
|
||||
"webhook": "{callback}"
|
||||
"webhook": "{callback}",
|
||||
"peer": "callback-1"
|
||||
}
|
||||
},
|
||||
"scenario": "silent",
|
||||
|
|
@ -421,6 +423,281 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a-001",
|
||||
"role": "slave",
|
||||
"title": "unsigned master command refused",
|
||||
"steps": [
|
||||
{
|
||||
"mail": {
|
||||
"to": "case",
|
||||
"text": "hi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pump": true
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "master",
|
||||
"sign": "none",
|
||||
"message": {
|
||||
"type": "mail.send",
|
||||
"case": "current",
|
||||
"in_reply_to": "{message_ref}",
|
||||
"body": "hello"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 401,
|
||||
"error": "unauthenticated-peer",
|
||||
"outbox_count": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a-002",
|
||||
"role": "slave",
|
||||
"title": "a mode cannot assign case ids",
|
||||
"steps": [
|
||||
{
|
||||
"mail": {
|
||||
"to": "case",
|
||||
"text": "hi"
|
||||
},
|
||||
"expect": {
|
||||
"response_status": "intake-reported"
|
||||
}
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "master",
|
||||
"as": "callback-1",
|
||||
"message": {
|
||||
"type": "case.assign",
|
||||
"intake_ref": "{intake_ref}",
|
||||
"resource_id": "EVIL-1"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 403,
|
||||
"error": "wrong-peer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pump": true,
|
||||
"expect": {
|
||||
"case_address": "case=EXT-100@modes.example"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a-003",
|
||||
"role": "slave",
|
||||
"title": "webhook to a peer email has no key for",
|
||||
"steps": [
|
||||
{
|
||||
"mail": {
|
||||
"to": "case",
|
||||
"text": "hi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pump": true
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "master",
|
||||
"as": "cms-1",
|
||||
"message": {
|
||||
"type": "case.policy",
|
||||
"case": "current",
|
||||
"transfers": {
|
||||
"webhook": "http://attacker.example/h",
|
||||
"peer": "mallory"
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 400,
|
||||
"error": "unknown-peer"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a-004",
|
||||
"role": "slave",
|
||||
"title": "replayed master command",
|
||||
"steps": [
|
||||
{
|
||||
"mail": {
|
||||
"to": "case",
|
||||
"text": "hi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pump": true
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "master",
|
||||
"as": "cms-1",
|
||||
"sign": "stale",
|
||||
"message": {
|
||||
"type": "mail.send",
|
||||
"case": "current",
|
||||
"in_reply_to": "{message_ref}",
|
||||
"body": "hello"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 401,
|
||||
"error": "replayed",
|
||||
"outbox_count": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a-005",
|
||||
"role": "slave",
|
||||
"title": "master command signed with the wrong key",
|
||||
"steps": [
|
||||
{
|
||||
"mail": {
|
||||
"to": "case",
|
||||
"text": "hi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pump": true
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "master",
|
||||
"as": "cms-1",
|
||||
"sign": "wrong-key",
|
||||
"message": {
|
||||
"type": "mail.send",
|
||||
"case": "current",
|
||||
"in_reply_to": "{message_ref}",
|
||||
"body": "hello"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 401,
|
||||
"error": "unauthenticated-peer",
|
||||
"outbox_count": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "master",
|
||||
"as": "cms-1",
|
||||
"message": {
|
||||
"type": "mail.send",
|
||||
"case": "current",
|
||||
"in_reply_to": "{message_ref}",
|
||||
"body": "hello"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 200,
|
||||
"outbox_count": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a-006",
|
||||
"role": "master",
|
||||
"title": "statuses only from the handoff's peer, at its URL",
|
||||
"scenario": "silent",
|
||||
"steps": [
|
||||
{
|
||||
"mail": {
|
||||
"to": "ticket!callback",
|
||||
"text": "Phone: +49 30 1234567\nTime: tomorrow 10:00"
|
||||
},
|
||||
"expect": {
|
||||
"outbox_count": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "status",
|
||||
"as": "cms-1",
|
||||
"message": {
|
||||
"type": "handoff.status",
|
||||
"case": "current",
|
||||
"seq": 1,
|
||||
"status": "completed"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 403,
|
||||
"error": "wrong-peer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "status",
|
||||
"as": "callback-1",
|
||||
"token": "bogus",
|
||||
"message": {
|
||||
"type": "handoff.status",
|
||||
"case": "current",
|
||||
"seq": 1,
|
||||
"status": "completed"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 404,
|
||||
"error": "unknown-handoff",
|
||||
"outbox_count": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "status",
|
||||
"as": "callback-1",
|
||||
"message": {
|
||||
"type": "handoff.status",
|
||||
"case": "current",
|
||||
"seq": 1,
|
||||
"status": "scheduled",
|
||||
"at": "x"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 200
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a-007",
|
||||
"role": "master",
|
||||
"title": "only the gateway posts events",
|
||||
"steps": [
|
||||
{
|
||||
"inject": {
|
||||
"endpoint": "events",
|
||||
"as": "callback-1",
|
||||
"message": {
|
||||
"type": "x"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"http_status": 403,
|
||||
"error": "wrong-peer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue