9ca9c43dfa
CB-575 already names the merged MCP-cancellation-filter change, so the charter-receipt comments used the wrong number. Retag to CB-571, the number this work was authored against.
137 lines
6.2 KiB
Java
137 lines
6.2 KiB
Java
package dev.ltms.bridged.member;
|
|
|
|
import ch.qos.logback.classic.Level;
|
|
import ch.qos.logback.classic.Logger;
|
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
|
import ch.qos.logback.core.read.ListAppender;
|
|
import dev.ltms.bridged.config.BridgedConfig;
|
|
import dev.ltms.bridged.herdr.AgentControl;
|
|
import dev.ltms.bridged.herdr.FakeHerdr;
|
|
import dev.ltms.bridged.herdr.WorkspaceControl;
|
|
import dev.ltms.bridged.peer.Capability;
|
|
import dev.ltms.bridged.peer.CharterReceipt;
|
|
import dev.ltms.bridged.peer.MemberRole;
|
|
import dev.ltms.bridged.peer.SpawnRequest;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
import java.util.function.Supplier;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
class HerdrPeerLauncherCharterTest {
|
|
|
|
@Test
|
|
void readsAndComposesTheFleetCharterForEachSpawn() {
|
|
AtomicReference<BridgedConfig.Fleet> fleet = new AtomicReference<>(fleet(Map.of()));
|
|
CapturingLauncher launcher = new CapturingLauncher(fleet::get);
|
|
|
|
launcher.spawn(new SpawnRequest("mcp", null, null, null, null, MemberRole.DEV));
|
|
fleet.set(fleet(Map.of("dev", "role charter")));
|
|
launcher.spawn(new SpawnRequest("mcp", null, null, null, null, MemberRole.DEV));
|
|
launcher.spawn(new SpawnRequest("no-mcp", null, null, null, null, MemberRole.DEV));
|
|
|
|
assertEquals(HerdrPeerLauncher.REPLY_CHARTER, launcher.specs.get(0).charter(),
|
|
"without a role charter, MCP profiles receive only the reply charter");
|
|
assertEquals("role charter\n\n" + HerdrPeerLauncher.REPLY_CHARTER, launcher.specs.get(1).charter(),
|
|
"the changed supplier value is read for the next spawn and the reply rule is last");
|
|
assertEquals("role charter", launcher.specs.get(2).charter(),
|
|
"a role charter does not depend on an MCP mount");
|
|
}
|
|
|
|
@Test
|
|
void panePlacementSpawnLogNeverContainsTheCharterText() {
|
|
// A pane-placement spawn used to log the whole argv (CB-571), and the charter travels
|
|
// inside argv — so the charter text leaked to the daemon log. Prove the legacy pane path
|
|
// now redacts it to its digest.
|
|
String secret = "TOP SECRET charter marker 99x"; // distinctive, so a leak is unambiguous
|
|
AtomicReference<BridgedConfig.Fleet> fleet = new AtomicReference<>(fleet(Map.of("dev", secret)));
|
|
CharterArgLauncher launcher = new CharterArgLauncher(fleet::get);
|
|
|
|
Logger logger = (Logger) LoggerFactory.getLogger(HerdrPeerLauncher.class);
|
|
Level previous = logger.getLevel();
|
|
ListAppender<ILoggingEvent> appender = new ListAppender<>();
|
|
appender.start();
|
|
logger.addAppender(appender);
|
|
logger.setLevel(Level.INFO); // the test logback sets dev.ltms.bridged to WARN; a leak lives at INFO
|
|
try {
|
|
launcher.spawn(new SpawnRequest("mcp", null, null, null, null, MemberRole.DEV));
|
|
|
|
String all = String.join("\n", appender.list.stream().map(ILoggingEvent::getFormattedMessage).toList());
|
|
assertFalse(all.contains(secret),
|
|
"the pane-placement spawn log must not contain the charter text; got:\n" + all);
|
|
// The "mcp" profile composes role + reply charter; the digest must match that composed
|
|
// string (the exact bytes the adapter receives), proving the redaction hashes and
|
|
// removes the real, full charter — not some placeholder.
|
|
String composed = secret + "\n\n" + HerdrPeerLauncher.REPLY_CHARTER;
|
|
assertTrue(all.contains("<charter sha256=" + CharterReceipt.digestOf(composed) + ">"),
|
|
"the charter argv argument should be replaced by its digest; got:\n" + all);
|
|
} finally {
|
|
logger.setLevel(previous);
|
|
logger.detachAppender(appender);
|
|
}
|
|
}
|
|
|
|
private static BridgedConfig.Fleet fleet(Map<String, String> charters) {
|
|
return new BridgedConfig.Fleet(Map.of(), Map.of(), Map.of(), Map.of(), charters, null);
|
|
}
|
|
|
|
private static BridgedConfig.Profile profile(String name, String mcpUrl) {
|
|
return new BridgedConfig.Profile(name, "http://gx00.gw:8000", null, null,
|
|
"BRIDGED_WORKER_TOKEN", List.of("test"), "pane", null, null, mcpUrl, null, null);
|
|
}
|
|
|
|
/**
|
|
* A launcher whose {@code buildLaunch} hands the composed charter to herdr as one argv element
|
|
* (what the claude-cod adapter does), so a pane-placement spawn log would print it unless the
|
|
* base redacts it.
|
|
*/
|
|
private static final class CharterArgLauncher extends HerdrPeerLauncher {
|
|
|
|
CharterArgLauncher(Supplier<BridgedConfig.Fleet> fleet) {
|
|
super("test", new AgentControl(new FakeHerdr()), new WorkspaceControl(new FakeHerdr()),
|
|
Map.of("mcp", profile("mcp", "http://bridge")),
|
|
"mcp", _ -> null, 0, () -> 0L, () -> { }, fleet);
|
|
}
|
|
|
|
@Override
|
|
protected Launch buildLaunch(BridgedConfig.Profile cfg, LaunchSpec spec) {
|
|
return new Launch(Map.of(), List.of("test", spec.charter() == null ? "none" : spec.charter()));
|
|
}
|
|
|
|
@Override
|
|
public Set<Capability> capabilities() {
|
|
return Set.of();
|
|
}
|
|
}
|
|
|
|
private static final class CapturingLauncher extends HerdrPeerLauncher {
|
|
private final List<LaunchSpec> specs = new ArrayList<>();
|
|
|
|
CapturingLauncher(Supplier<BridgedConfig.Fleet> fleet) {
|
|
super("test", new AgentControl(new FakeHerdr()), new WorkspaceControl(new FakeHerdr()),
|
|
Map.of("mcp", profile("mcp", "http://bridge"),
|
|
"no-mcp", profile("no-mcp", null)),
|
|
"mcp", _ -> null, 0, () -> 0L, () -> { }, fleet);
|
|
}
|
|
|
|
@Override
|
|
protected Launch buildLaunch(BridgedConfig.Profile cfg, LaunchSpec spec) {
|
|
specs.add(spec);
|
|
return new Launch(Map.of(), List.of("test"));
|
|
}
|
|
|
|
@Override
|
|
public Set<Capability> capabilities() {
|
|
return Set.of();
|
|
}
|
|
}
|
|
}
|