Hear a restricted stream only from a module's own account, never the tool runner's (issue 727)
The tool runner's bus account carried the heard grants, consumer and bind word of every module it runs, and every agent session on that machine reaches the bus as that account, so any session could pull a granted module's consumer on MAIL. Per ADR 0376 §1 the controller now composes a restricted stream's consumer, grants and bind word only for a module that declares runs-as; registration refuses a consumes of a restricted stream without runs-as (§2), and a grant that composes nothing is said in doctor (D-heard) and in the controller's log on every raise and send.
This commit is contained in:
@@ -247,6 +247,8 @@ func CatalogueProblems(shelf Shelf) []string {
|
||||
var problems []string
|
||||
// A stream name has one owner (novox/hq ADR 0363).
|
||||
problems = append(problems, StreamNameProblems(shelf)...)
|
||||
// A module that hears a restricted stream runs as an account of its own (novox/hq ADR 0376 §2).
|
||||
problems = append(problems, RestrictedHearingProblems(shelf)...)
|
||||
|
||||
// Who declares what, and who declared it first.
|
||||
declaredBy := map[string]string{}
|
||||
|
||||
@@ -311,6 +311,41 @@ func StreamNameProblems(shelf Shelf) []string {
|
||||
return problems
|
||||
}
|
||||
|
||||
// RestrictedHearingProblems refuses a module that would hear a restricted stream without an account of its own
|
||||
// (novox/hq ADR 0376 §2): a `consumes` naming a module literally and asking for events a restricted stream of
|
||||
// that module carries, by a manifest that declares no `runs-as`. A module the machine's tool runner carries
|
||||
// reaches the bus as the tool runner's account, which every agent session there shares, so the controller would
|
||||
// compose nothing for it. Judged only where the provider is on the shelf, as a consumer may be installed before
|
||||
// its emitter; a wildcard emitter never reaches a module's stream (ADR 0363 §5) and is not judged.
|
||||
func RestrictedHearingProblems(shelf Shelf) []string {
|
||||
var problems []string
|
||||
for _, module := range shelfOrder(shelf) {
|
||||
m := shelf[module]
|
||||
if m.RunsAs != "" {
|
||||
continue
|
||||
}
|
||||
for _, c := range m.Consumes {
|
||||
emitter, rest, named := strings.Cut(c, ".")
|
||||
provider, known := shelf[emitter]
|
||||
if !named || emitter == OneName || emitter == TheRest || !known {
|
||||
continue
|
||||
}
|
||||
hearing, _ := provider.StreamsHearing(rest)
|
||||
for _, s := range hearing {
|
||||
if s.Grant == nil {
|
||||
continue
|
||||
}
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s consumes %s, which %s's restricted stream %s carries, so %s must run as an account of its "+
|
||||
"own (runs-as): the machine's runtime runs as the operator's account, which every agent "+
|
||||
"runs as, and never hears a restricted stream (novox/hq ADR 0376)",
|
||||
module, c, emitter, s.BusName(), module))
|
||||
}
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
// GrantedKeys reads a restricting config value (novox/hq ADR 0363 §4): each key whose list names the
|
||||
// module wherever it runs, or the module on this machine as `<machine>.<module>`. A value of another shape
|
||||
// grants nothing; a key that is not one usable name grants nothing.
|
||||
|
||||
@@ -204,3 +204,41 @@ func TestNarrowingTwoPatterns(t *testing.T) {
|
||||
t.Fatalf("uncovered: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// **A module that hears a restricted stream runs as an account of its own** (ADR 0376 §2): registration refuses a
|
||||
// `consumes` naming a module and asking for events its restricted stream carries, unless the manifest declares
|
||||
// `runs-as`; a stream without a grant, an event the provider says on EVENTS, and a wildcard emitter stay as they
|
||||
// were, and a consumer whose provider is not on the shelf is judged once it is.
|
||||
func TestHearingARestrictedStreamNeedsAnAccountOfItsOwn(t *testing.T) {
|
||||
provider, err := ParseManifest([]byte(mailManifest(t, mailStream, `"emits":["account.added"],`)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
feed := Manifest{Module: "feed", Streams: []StreamDeclaration{{Name: "items", Events: []string{"item.**"}}}}
|
||||
carried := Manifest{Module: "reader", Consumes: []string{"mesh-mail.mail.*.message.added"}}
|
||||
got := RestrictedHearingProblems(Shelf{"mesh-mail": provider, "reader": carried})
|
||||
if len(got) != 1 || !strings.Contains(got[0], "reader consumes mesh-mail.mail.*.message.added") ||
|
||||
!strings.Contains(got[0], "MAIL") || !strings.Contains(got[0], "runs-as") || !strings.Contains(got[0], "ADR 0376") {
|
||||
t.Fatalf("a carried module hearing MAIL was not refused as it should be: %v", got)
|
||||
}
|
||||
if all := strings.Join(CatalogueProblems(Shelf{"mesh-mail": provider, "reader": carried}), "; "); !strings.Contains(all, got[0]) {
|
||||
t.Fatalf("the catalogue's check does not refuse it: %s", all)
|
||||
}
|
||||
own := carried
|
||||
own.RunsAs = "reader"
|
||||
if got := RestrictedHearingProblems(Shelf{"mesh-mail": provider, "reader": own}); len(got) != 0 {
|
||||
t.Fatalf("a module of its own account was refused: %v", got)
|
||||
}
|
||||
for _, m := range []Manifest{
|
||||
{Module: "events", Consumes: []string{"mesh-mail.account.added"}},
|
||||
{Module: "items", Consumes: []string{"feed.item.**"}},
|
||||
{Module: "audit", Consumes: []string{"**", "*.mail.**"}},
|
||||
} {
|
||||
if got := RestrictedHearingProblems(Shelf{"mesh-mail": provider, "feed": feed, m.Module: m}); len(got) != 0 {
|
||||
t.Errorf("%s was refused: %v", m.Module, got)
|
||||
}
|
||||
}
|
||||
if got := RestrictedHearingProblems(Shelf{"reader": carried}); len(got) != 0 {
|
||||
t.Fatalf("judged without its provider: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user