test(evals): isolate private device authorization browser steps

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-27 06:51:00 -05:00
1 parent 30f854b0bc
commit beca0672fb
6 files changed
+159 -21

No files matched your search

@@ -1,13 +1,15 @@
import { expect } from "@playwright/test";
import type { ChatCatalogContext } from "../chat-catalog-contract.js";
import { pollUntil } from "../api.js";
import { definition, externalObservation, observeExternal, checks, verifyRenderedReply } from "./external-support.js";
import { waitForReply } from "./identity-support.js";
import { oauthPost, mcp, origin, resource, type Tokens } from "../public-mcp-client.js";
import { privateBrowserEntry } from "./external-private-browser.js";
import { matchesDevicePrivateReceipt } from "./external-device-private-contract.js";
const DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
interface DeviceCode { device_code: string; user_code: string; verification_uri: string; verification_uri_complete: string; expires_in: number; interval: number }
interface DeviceFixture { clientId: string; otherClientId: string; primary: DeviceCode; expiring: DeviceCode; expiresAt: number; polledAt: number; tokens?: Tokens }
interface DeviceFixture { personId: string; clientId: string; otherClientId: string; primary: DeviceCode; expiring: DeviceCode; expiresAt: number; polledAt: number; tokens?: Tokens }
const fixtures = new WeakMap<ChatCatalogContext, DeviceFixture>();
async function requestDevice(clientId: string, companyId: string): Promise<DeviceCode> {
const response = await oauthPost("device_authorization", { client_id: clientId, resource, company_id: companyId, scope: "paperclip:read offline_access" });
@@ -28,6 +30,8 @@ export function gradeDeviceLogin(value: unknown) {
c.add("correct-human-verification", o.state.verificationPath === "/mcp-device" && o.state.currentCodeVisible === true && o.state.companyConsentVisible === true && o.state.readScopeVisible === true
&& o.state.humanApproved === true && o.state.clientCompanyId === o.companyId && o.state.clientReadOnly === true,
"The person follows the issuer's actual page/current code and authorizes exactly the intended company and read scope.");
c.add("private-device-ui", matchesDevicePrivateReceipt("device-approve", o.state.approvalUi) && matchesDevicePrivateReceipt("device-expired", o.state.expiredUi),
"Both actual device UI phases use the isolated finite browser with closed receipts, outside Test reporting.");
c.add("pending-without-human-approval", o.state.pendingError === "authorization_pending", "The waiting CLI cannot obtain credentials before human approval.");
c.add("resource-and-client-mismatch-denied", o.state.wrongResourceDenied === true && o.state.wrongClientDenied === true && o.state.correctExchangeSucceeded === true,
"Mismatched resource/client requests fail without consuming the valid selected grant.");
@@ -44,25 +48,33 @@ export const assistantDeviceExternalCases = [definition("assistant-connections.d
const setup = await ctx.input.api.get<{ enabled: boolean; serverUrl: string }>("/api/mcp/setup");
if (!setup.enabled || setup.serverUrl !== resource) throw new Error("Setup blocker: this disposable instance has no compatible public MCP issuer/resource");
const clientId = await registerClient(), otherClientId = await registerClient();
const primary = await requestDevice(clientId, ctx.input.fixtures.company.id), expiring = await requestDevice(clientId, ctx.input.fixtures.company.id);
const primary = await requestDevice(clientId, ctx.input.fixtures.company.id);
ctx.input.privateSecrets.push(primary.device_code, primary.user_code, primary.verification_uri_complete);
const expiring = await requestDevice(clientId, ctx.input.fixtures.company.id);
ctx.input.privateSecrets.push(expiring.device_code, expiring.user_code, expiring.verification_uri_complete);
if (primary.expires_in !== 600 || expiring.expires_in !== 600 || primary.verification_uri !== `${origin}/mcp-device`) throw new Error("Declared bounded device-code lifetime or verification origin differs from the real issuer");
ctx.input.privateSecrets.push(primary.device_code, primary.user_code, expiring.device_code, expiring.user_code);
const session = await ctx.input.api.get<{ user: { id: string } }>("/api/auth/get-session");
if (!session.user?.id) throw new Error("Device UI requires the authenticated fixture person");
const expiresAt = Date.now() + expiring.expires_in * 1000;
const fixture = { clientId, otherClientId, primary, expiring, expiresAt, polledAt: Date.now() }; fixtures.set(ctx, fixture);
const fixture = { personId: session.user.id, clientId, otherClientId, primary, expiring, expiresAt, polledAt: Date.now() }; fixtures.set(ctx, fixture);
const pending = await exchange(fixture, primary); const pendingBody = await pending.json() as { error?: string };
return { verificationPath: new URL(primary.verification_uri).pathname, pendingError: pendingBody.error, expirationWaitMs: 600_000 };
}, gradeDeviceLogin, {
authentication: "human-session", attemptTimeoutMs: 780_000,
sourceFiles: ["chat-catalog-domains/external-assistant-device.ts", "chat-catalog-domains/external-support.ts", "chat-catalog-domains/internal-support.ts", "chat-catalog-domains/identity-support.ts", "public-mcp-client.ts", "chat-scenarios/assistant-connections.yaml"],
sourceFiles: ["chat-catalog-domains/external-assistant-device.ts", "chat-catalog-domains/external-private-browser.ts", "chat-catalog-domains/external-private-browser-worker.ts", "chat-catalog-domains/external-device-private-contract.ts", "../../ui/src/pages/McpConnect.tsx", "../../server/src/routes/public-mcp.ts", "../../server/src/services/public-mcp/oauth.ts", "../../packages/shared/src/public-mcp.ts", "chat-catalog-domains/external-support.ts", "chat-catalog-domains/internal-support.ts", "chat-catalog-domains/identity-support.ts", "public-mcp-client.ts", "chat-scenarios/assistant-connections.yaml"],
async act(ctx, state) {
await waitForReply(ctx); const fixture = fixtures.get(ctx); if (!fixture) throw new Error("Device fixture missing");
const source = ctx.input.page.url(); await ctx.input.page.goto(fixture.primary.verification_uri_complete);
await expect(ctx.input.page.getByRole("heading", { name: /^Connect .+ to Paperclip$/ })).toBeVisible();
await expect(ctx.input.page.getByText(fixture.primary.user_code.toUpperCase(), { exact: true })).toBeVisible(); state.currentCodeVisible = true;
await expect(ctx.input.page.getByText(ctx.input.fixtures.company.name, { exact: true })).toBeVisible(); state.companyConsentVisible = true;
await expect(ctx.input.page.getByText("Read all of your Paperclip data", { exact: true })).toBeVisible(); state.readScopeVisible = true;
await ctx.input.page.getByRole("button", { name: "Connect organization", exact: true }).click();
await expect(ctx.input.page.getByText(/connected|approved/i).first()).toBeVisible(); state.humanApproved = true;
const approvalUi = await privateBrowserEntry({
mode: "device-approve", baseURL: origin, setupURL: fixture.primary.verification_uri_complete,
providerURL: origin, credential: fixture.primary.user_code, companyId: ctx.input.fixtures.company.id,
device: { userId: fixture.personId, companyName: ctx.input.fixtures.company.name },
cookies: await ctx.input.page.context().cookies(origin),
});
state.approvalUi = approvalUi;
state.currentCodeVisible = approvalUi.currentCodeVisible;
state.companyConsentVisible = approvalUi.companyConsentVisible;
state.readScopeVisible = approvalUi.readScopeVisible;
state.humanApproved = approvalUi.humanApproved;
const wrongResource = await exchange(fixture, fixture.primary, { resource: origin + "/mcp/not-the-resource" }); state.wrongResourceDenied = wrongResource.status === 400; await wrongResource.body?.cancel();
const wrongClient = await exchange(fixture, fixture.primary, { client_id: fixture.otherClientId }); state.wrongClientDenied = wrongClient.status === 400; await wrongClient.body?.cancel();
if (Date.now() < fixture.polledAt + fixture.primary.interval * 1000) await pollUntil({ label: "advertised device polling interval", deadlineAt: Date.now() + 15_000, intervalMs: 200, load: async () => Date.now(), accept: now => now >= fixture.polledAt + fixture.primary.interval * 1000 });
@@ -81,9 +93,14 @@ export const assistantDeviceExternalCases = [definition("assistant-connections.d
state.elapsedBeforeExpiryProbe = Date.now() - (fixture.expiresAt - 600_000);
const expired = await exchange(fixture, fixture.expiring); const body = await expired.json() as { error?: string };
state.expiredCodeDenied = expired.status === 400 && body.error === "expired_token";
await ctx.input.page.goto(fixture.expiring.verification_uri_complete);
await expect(ctx.input.page.getByText(/expired|invalid/i).first()).toBeVisible(); state.expiredPageVisible = true;
await ctx.input.page.goto(source, { waitUntil: "domcontentloaded" });
const expiredUi = await privateBrowserEntry({
mode: "device-expired", baseURL: origin, setupURL: fixture.expiring.verification_uri_complete,
providerURL: origin, credential: fixture.expiring.user_code, companyId: ctx.input.fixtures.company.id,
device: { userId: fixture.personId, companyName: ctx.input.fixtures.company.name },
cookies: await ctx.input.page.context().cookies(origin),
});
state.expiredUi = expiredUi;
state.expiredPageVisible = expiredUi.expiredPageVisible;
},
async observe(ctx, state) { return observeExternal(ctx, state); },
async verifyResult(ctx, _state, value) { await verifyRenderedReply(ctx, value); },
@@ -0,0 +1,36 @@
import { expect, it } from "vitest";
import { matchesDevicePrivateReceipt, validDevicePrivateInput } from "./external-device-private-contract.js";
const base = "http://127.0.0.1:3100";
const code = "ABCD-EFGH"; // Synthetic calibration only, never a provider-issued code.
const url = `${base}/mcp-device?user_code=${code}`;
const company = "11111111-1111-4111-8111-111111111111";
const device = { userId: "fixture-person", companyName: "Owned company" };
it("allows only the exact owned device page and current code for two finite phases", () => {
for (const mode of ["device-approve", "device-expired"]) {
expect(validDevicePrivateInput(mode, base, url, company, device, code)).toBe(true);
for (const target of [
url.replace("127.0.0.1", "example.test"), url.replace("http:", "https:"),
url.replace("/mcp-device", "/mcp-connect"), url + "&next=/other", url + "#secret",
url.replace(code, "WXYZ-1234"), url + `&user_code=${code}`,
url.replace("http://", "http://user:password@"),
]) expect(validDevicePrivateInput(mode, base, target, company, device, code)).toBe(false);
}
expect(validDevicePrivateInput("arbitrary", base, url, company, device, code)).toBe(false);
expect(validDevicePrivateInput("device-approve", base, url, company, undefined, code)).toBe(false);
expect(validDevicePrivateInput("device-approve", base, url, "wrong", device, code)).toBe(false);
expect(validDevicePrivateInput("device-approve", base, url, company, { ...device, userId: "" }, code)).toBe(false);
expect(validDevicePrivateInput("device-approve", base, url, company, device, "bad\r\ncode")).toBe(false);
});
it("accepts only complete boolean-only private receipts, never codes, URLs or forged capture claims", () => {
const boundary = { processIsolation: true, testRunnerAttached: false, tracingEnabled: false, videoEnabled: false, apiStepRecorder: false, deviceSessionVerified: true };
const approval = { ...boundary, currentCodeVisible: true, companyConsentVisible: true, readScopeVisible: true, humanApproved: true };
const expired = { ...boundary, expiredPageVisible: true };
expect(matchesDevicePrivateReceipt("device-approve", approval)).toBe(true);
expect(matchesDevicePrivateReceipt("device-expired", expired)).toBe(true);
for (const wrong of [undefined, {}, expired, { ...approval, tracingEnabled: true }, { ...approval, deviceSessionVerified: false }, { ...approval, currentCodeVisible: false }, { ...approval, userCode: code }, { ...approval, verificationUri: url }, { ...approval, humanApproved: "true" }])
expect(matchesDevicePrivateReceipt("device-approve", wrong)).toBe(false);
expect(matchesDevicePrivateReceipt("device-expired", { ...expired, expiredPageVisible: false })).toBe(false);
});
@@ -0,0 +1,46 @@
export interface DevicePrivateInput {
userId: string;
companyName: string;
}
/** Only the owned issuer's exact device-code UI is allowed, never arbitrary
* navigation. The current code and complete URL remain private worker input. */
export function validDevicePrivateInput(
mode: string,
baseURL: string,
setupURL: string,
companyId: unknown,
device: DevicePrivateInput | undefined,
userCode: string,
) {
try {
const base = new URL(baseURL), setup = new URL(setupURL);
return ["device-approve", "device-expired"].includes(mode)
&& base.protocol === "http:" && base.hostname === "127.0.0.1"
&& !base.username && !base.password && base.pathname === "/" && !base.search && !base.hash
&& setup.origin === base.origin && !setup.username && !setup.password
&& setup.pathname === "/mcp-device" && !setup.hash
&& setup.searchParams.size === 1 && setup.searchParams.get("user_code") === userCode
&& /^[A-Za-z0-9 -]{8,12}$/.test(userCode)
&& typeof companyId === "string" && /^[a-f0-9-]{36}$/i.test(companyId)
&& !!device && typeof device.userId === "string" && device.userId.length > 0 && device.userId.length <= 256
&& typeof device.companyName === "string" && device.companyName.trim().length > 0 && device.companyName.length <= 256
&& !/[\r\n]/.test(device.userId + device.companyName);
} catch { return false; }
}
export function matchesDevicePrivateReceipt(mode: string, value: unknown) {
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
const receipt = value as Record<string, unknown>;
const expected = {
processIsolation: true, testRunnerAttached: false, tracingEnabled: false,
videoEnabled: false, apiStepRecorder: false, deviceSessionVerified: true,
...(mode === "device-approve" ? {
currentCodeVisible: true, companyConsentVisible: true, readScopeVisible: true,
humanApproved: true,
} : mode === "device-expired" ? { expiredPageVisible: true } : {}),
};
return ["device-approve", "device-expired"].includes(mode)
&& Object.keys(receipt).length === Object.keys(expected).length
&& Object.entries(expected).every(([key, expectedValue]) => receipt[key] === expectedValue);
}
@@ -1,23 +1,57 @@
import { validDevicePrivateInput, type DevicePrivateInput } from "./external-device-private-contract.js";
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { chromium, test, type Cookie } from "@playwright/test";
import { validGrokRoutinePrivateInput, matchesConfiguredGrokRoutine, type GrokRoutinePrivateInput } from "./external-grok-private-contract.js";
import { validSlackIdentityInput, validSlackWizardIdentityInput, matchingSlackIdentityPreview, type SlackIdentityPrivateInput } from "./external-slack-identity-contract.js";
import { validSlackSetupPrivate, type SlackSetupPrivateInput } from "./external-slack-setup-private-contract.js";
interface Request { mode: "secure-key" | "canary" | "gateway-token-rotation" | "company-secret-create" | "personal-secret-create" | "grok-routine-configure" | "slack-identity-confirm" | "slack-endpoint-configure" | "slack-wizard-confirm" | "slack-wizard-resume" | "slack-endpoint-reconnect"; slackSetup?:SlackSetupPrivateInput; slackIdentity?:SlackIdentityPrivateInput; grok?:GrokRoutinePrivateInput; companyId?:string; personal?:{definitionId:string;userId:string}; gateway?: { id: string; oldTokenId: string; otherToken: string; endpointPath: string; readName: string }; baseURL: string; setupURL: string; providerURL: string; credential: string; cookies: Cookie[]; executablePath?: string; channel?: string }
interface Request { mode: "secure-key" | "canary" | "gateway-token-rotation" | "company-secret-create" | "personal-secret-create" | "grok-routine-configure" | "slack-identity-confirm" | "slack-endpoint-configure" | "slack-wizard-confirm" | "slack-wizard-resume" | "slack-endpoint-reconnect" | "device-approve" | "device-expired"; device?: DevicePrivateInput; slackSetup?:SlackSetupPrivateInput; slackIdentity?:SlackIdentityPrivateInput; grok?:GrokRoutinePrivateInput; companyId?:string; personal?:{definitionId:string;userId:string}; gateway?: { id: string; oldTokenId: string; otherToken: string; endpointPath: string; readName: string }; baseURL: string; setupURL: string; providerURL: string; credential: string; cookies: Cookie[]; executablePath?: string; channel?: string }
/** Separate Node process: no Playwright Test worker, API-step listener or artifact
* recorder exists. This module never enables tracing, video or screenshots. */
async function main() {
let attached = false; try { test.info(); attached = true; } catch { /* expected outside Test */ }
if (attached) throw new Error("Private browser worker cannot run inside Playwright Test");
const input = JSON.parse(await readFile(process.argv[2]!, "utf8")) as Request;
if (!input.credential || input.credential.length > (input.mode === "grok-routine-configure" ? 16384 : 8192) || !["secure-key", "canary", "gateway-token-rotation", "company-secret-create", "personal-secret-create", "grok-routine-configure", "slack-identity-confirm", "slack-endpoint-configure", "slack-wizard-confirm", "slack-wizard-resume", "slack-endpoint-reconnect"].includes(input.mode)) throw new Error("Invalid private browser request");
if (!input.credential || input.credential.length > (input.mode === "grok-routine-configure" ? 16384 : 8192) || !["secure-key", "canary", "gateway-token-rotation", "company-secret-create", "personal-secret-create", "grok-routine-configure", "slack-identity-confirm", "slack-endpoint-configure", "slack-wizard-confirm", "slack-wizard-resume", "slack-endpoint-reconnect", "device-approve", "device-expired"].includes(input.mode)) throw new Error("Invalid private browser request");
const base = new URL(input.baseURL);
if (base.hostname !== "127.0.0.1" || base.protocol !== "http:" || new URL(input.setupURL).origin !== base.origin) throw new Error("Private browser target is outside the disposable loopback instance");
const browser = await chromium.launch({ headless: true, ...(input.executablePath ? { executablePath: input.executablePath } : {}), ...(input.channel ? { channel: input.channel } : {}) });
try {
const context = await browser.newContext({ baseURL: base.origin });
await context.addCookies(input.cookies); const page = await context.newPage(); page.setDefaultTimeout(30_000);
if (input.mode === "device-approve" || input.mode === "device-expired") {
if (!validDevicePrivateInput(input.mode, input.baseURL, input.setupURL, input.companyId, input.device, input.credential) || !input.device) throw new Error("Invalid finite device verification request");
const session = await context.request.get("/api/auth/get-session");
if (!session.ok() || (await session.json()).user?.id !== input.device.userId) throw new Error("Device UI requires the same signed-in fixture person");
const description = page.waitForResponse(response => {
const url = new URL(response.url());
return url.origin === base.origin && url.pathname === "/api/mcp/device" && url.searchParams.get("user_code") === input.credential && response.request().method() === "GET";
});
await page.goto(input.setupURL, { waitUntil: "domcontentloaded" });
const described = await description;
if (input.mode === "device-expired") {
const body = await described.json();
if (described.status() !== 404 || body.error !== "invalid_request") throw new Error("Expired device UI did not receive the actual issuer rejection");
await page.getByText(/expired|invalid/i).first().waitFor();
if (await page.getByRole("button", { name: "Connect organization", exact: true }).isVisible()) throw new Error("Expired device still offers approval");
return { processIsolation: true, testRunnerAttached: false, tracingEnabled: false, videoEnabled: false, apiStepRecorder: false, deviceSessionVerified: true, expiredPageVisible: true };
}
if (!described.ok()) throw new Error("Device consent description unavailable");
const request = await described.json();
if (request.clientName !== "Paperclip disposable CLI" || request.requestedCompanyId !== input.companyId || request.requiresSignIn !== false || request.requestedWrite !== false || request.requestedConfigure !== false || request.agentConnection === true
|| !request.companies?.some((company: { id: string; name: string }) => company.id === input.companyId && company.name === input.device!.companyName)) throw new Error("Device consent does not match the exact owned read-only request");
await page.getByRole("heading", { name: "Connect Paperclip disposable CLI to Paperclip", exact: true }).waitFor();
await page.getByText(input.credential.toUpperCase(), { exact: true }).waitFor();
await page.getByText(input.device.companyName, { exact: true }).waitFor();
await page.getByText("Read all of your Paperclip data", { exact: true }).waitFor();
if (await page.getByText("Write all of your Paperclip data", { exact: true }).isVisible()) throw new Error("Device read-only request unexpectedly offers write authority");
const approval = page.waitForResponse(response => new URL(response.url()).origin === base.origin && new URL(response.url()).pathname === "/api/mcp/device/consent" && response.request().method() === "POST");
await page.getByRole("button", { name: "Connect organization", exact: true }).click();
const approved = await approval, submitted = approved.request().postDataJSON();
if (!approved.ok() || (await approved.json()).status !== "approved" || submitted.userCode !== input.credential || submitted.companyId !== input.companyId || submitted.decision !== "approve" || submitted.allowWrites !== false || submitted.allowConfiguration !== false) throw new Error("Actual device UI did not approve exactly the selected read-only company/code");
await page.getByRole("heading", { name: "Access approved", exact: true }).waitFor();
return { processIsolation: true, testRunnerAttached: false, tracingEnabled: false, videoEnabled: false, apiStepRecorder: false, deviceSessionVerified: true, currentCodeVisible: true, companyConsentVisible: true, readScopeVisible: true, humanApproved: true };
}
if (input.mode === "canary") {
await page.setContent('<input type="password" aria-label="App key" autocomplete="off">');
const field = page.getByLabel("App key"); await field.fill(input.credential);
@@ -1,3 +1,4 @@
import { validDevicePrivateInput, matchesDevicePrivateReceipt, type DevicePrivateInput } from "./external-device-private-contract.js";
import { mkdtemp, writeFile, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
@@ -8,8 +9,9 @@ import { chromium, type Cookie } from "@playwright/test";
import type { GrokRoutinePrivateInput } from "./external-grok-private-contract.js";
import type { SlackIdentityPrivateInput } from "./external-slack-identity-contract.js";
import type { SlackSetupPrivateInput } from "./external-slack-setup-private-contract.js";
export interface PrivateBrowserReceipt { processIsolation: true; testRunnerAttached: false; tracingEnabled: false; videoEnabled: false; apiStepRecorder: false; credentialEntered?: boolean; connectionId?: string; challengeVisible?: boolean; passwordInput?: boolean; autocompleteOff?: boolean; humanSubmittedSecurely?: boolean; visibleReady?: boolean; storageClean?: boolean; newTokenId?: string; tokenUiVisible?: boolean; newWorkedBeforeRevocation?: boolean; oldWorkedUntilRevocation?: boolean; oldRevoked?: boolean; newWorksAfter?: boolean; oldStatusAfter?: number; otherWorksAfter?: boolean; secretId?:string; secretUiVisible?:boolean; bindingId?:string; routineConfigured?:boolean; routineUiVisible?:boolean; identityConfirmed?:boolean; identityUiVisible?:boolean; principalId?:string; paperclipUserId?:string; endpointId?:string; slackCredentialUiVisible?:boolean; activationUiVisible?:boolean; endpointActive?:boolean; identityWasAlreadyLinked?:boolean; credentialUiRejected?:boolean }
export async function privateBrowserEntry(input: { mode: "secure-key" | "canary" | "gateway-token-rotation" | "company-secret-create" | "personal-secret-create" | "grok-routine-configure" | "slack-identity-confirm" | "slack-endpoint-configure" | "slack-wizard-confirm" | "slack-wizard-resume" | "slack-endpoint-reconnect"; slackSetup?:SlackSetupPrivateInput; slackIdentity?:SlackIdentityPrivateInput; grok?:GrokRoutinePrivateInput; companyId?:string; personal?:{definitionId:string;userId:string}; gateway?: { id: string; oldTokenId: string; otherToken: string; endpointPath: string; readName: string }; baseURL: string; setupURL: string; providerURL: string; credential: string; cookies: Cookie[] }, onToken?: (token: { id: string; token: string }) => void) {
export interface PrivateBrowserReceipt { processIsolation: true; testRunnerAttached: false; tracingEnabled: false; videoEnabled: false; apiStepRecorder: false; credentialEntered?: boolean; connectionId?: string; challengeVisible?: boolean; passwordInput?: boolean; autocompleteOff?: boolean; humanSubmittedSecurely?: boolean; visibleReady?: boolean; storageClean?: boolean; newTokenId?: string; tokenUiVisible?: boolean; newWorkedBeforeRevocation?: boolean; oldWorkedUntilRevocation?: boolean; oldRevoked?: boolean; newWorksAfter?: boolean; oldStatusAfter?: number; otherWorksAfter?: boolean; secretId?:string; secretUiVisible?:boolean; bindingId?:string; routineConfigured?:boolean; routineUiVisible?:boolean; identityConfirmed?:boolean; identityUiVisible?:boolean; principalId?:string; paperclipUserId?:string; endpointId?:string; slackCredentialUiVisible?:boolean; activationUiVisible?:boolean; endpointActive?:boolean; identityWasAlreadyLinked?:boolean; credentialUiRejected?:boolean; deviceSessionVerified?:boolean; currentCodeVisible?:boolean; companyConsentVisible?:boolean; readScopeVisible?:boolean; humanApproved?:boolean; expiredPageVisible?:boolean }
export async function privateBrowserEntry(input: { mode: "secure-key" | "canary" | "gateway-token-rotation" | "company-secret-create" | "personal-secret-create" | "grok-routine-configure" | "slack-identity-confirm" | "slack-endpoint-configure" | "slack-wizard-confirm" | "slack-wizard-resume" | "slack-endpoint-reconnect" | "device-approve" | "device-expired"; device?: DevicePrivateInput; slackSetup?:SlackSetupPrivateInput; slackIdentity?:SlackIdentityPrivateInput; grok?:GrokRoutinePrivateInput; companyId?:string; personal?:{definitionId:string;userId:string}; gateway?: { id: string; oldTokenId: string; otherToken: string; endpointPath: string; readName: string }; baseURL: string; setupURL: string; providerURL: string; credential: string; cookies: Cookie[] }, onToken?: (token: { id: string; token: string }) => void) {
if (input.mode.startsWith("device-") && !validDevicePrivateInput(input.mode, input.baseURL, input.setupURL, input.companyId, input.device, input.credential)) throw new Error("Invalid finite private device UI operation");
const directory = await mkdtemp(path.join(tmpdir(), "paperclip-private-browser-"));
try {
const request = path.join(directory, "request.json");
@@ -24,7 +26,8 @@ export async function privateBrowserEntry(input: { mode: "secure-key" | "canary"
} catch { throw new Error("Private browser entry failed; child output and credential-bearing request withheld"); }
let receipt: PrivateBrowserReceipt; try { receipt = JSON.parse(output) as PrivateBrowserReceipt; } catch { throw new Error("Private browser returned invalid closed receipt"); }
if (receipt.processIsolation !== true || receipt.testRunnerAttached !== false || receipt.tracingEnabled !== false || receipt.videoEnabled !== false || receipt.apiStepRecorder !== false
|| Object.keys(receipt).some(key => !["processIsolation", "testRunnerAttached", "tracingEnabled", "videoEnabled", "apiStepRecorder", "credentialEntered", "connectionId", "challengeVisible", "passwordInput", "autocompleteOff", "humanSubmittedSecurely", "visibleReady", "storageClean", "newTokenId", "tokenUiVisible", "newWorkedBeforeRevocation", "oldWorkedUntilRevocation", "oldRevoked", "newWorksAfter", "oldStatusAfter", "otherWorksAfter", "secretId", "secretUiVisible", "bindingId", "routineConfigured", "routineUiVisible", "identityConfirmed", "identityUiVisible", "principalId", "paperclipUserId", "endpointId", "slackCredentialUiVisible", "activationUiVisible", "endpointActive", "identityWasAlreadyLinked", "credentialUiRejected"].includes(key))) throw new Error("Private browser receipt exceeds its closed contract");
|| Object.keys(receipt).some(key => !["processIsolation", "testRunnerAttached", "tracingEnabled", "videoEnabled", "apiStepRecorder", "credentialEntered", "connectionId", "challengeVisible", "passwordInput", "autocompleteOff", "humanSubmittedSecurely", "visibleReady", "storageClean", "newTokenId", "tokenUiVisible", "newWorkedBeforeRevocation", "oldWorkedUntilRevocation", "oldRevoked", "newWorksAfter", "oldStatusAfter", "otherWorksAfter", "secretId", "secretUiVisible", "bindingId", "routineConfigured", "routineUiVisible", "identityConfirmed", "identityUiVisible", "principalId", "paperclipUserId", "endpointId", "slackCredentialUiVisible", "activationUiVisible", "endpointActive", "identityWasAlreadyLinked", "credentialUiRejected", "deviceSessionVerified", "currentCodeVisible", "companyConsentVisible", "readScopeVisible", "humanApproved", "expiredPageVisible"].includes(key))) throw new Error("Private browser receipt exceeds its closed contract");
if (input.mode.startsWith("device-") && !matchesDevicePrivateReceipt(input.mode, receipt)) throw new Error("Invalid closed device UI receipt");
if ((input.mode === "slack-wizard-confirm" || input.mode === "slack-wizard-resume") && (receipt.endpointId !== input.slackIdentity?.endpointId || receipt.principalId !== input.slackIdentity?.principalId || receipt.paperclipUserId !== input.slackIdentity?.userId || receipt.identityConfirmed !== true || receipt.activationUiVisible !== true || receipt.endpointActive !== true)) throw new Error("Invalid closed Slack wizard completion receipt");
if ((input.mode === "slack-endpoint-configure" || input.mode === "slack-endpoint-reconnect") && (receipt.endpointId !== input.slackSetup?.endpointId || receipt.slackCredentialUiVisible !== true || (receipt.credentialUiRejected === true ? receipt.humanSubmittedSecurely !== false : receipt.humanSubmittedSecurely !== true || receipt.storageClean !== true))) throw new Error("Invalid closed Slack credential form receipt");
if (input.mode === "slack-identity-confirm" && (receipt.identityConfirmed !== true || receipt.identityUiVisible !== true || receipt.principalId !== input.slackIdentity?.principalId || receipt.paperclipUserId !== input.slackIdentity?.userId)) throw new Error("Invalid closed Slack identity confirmation receipt");
@@ -59,6 +59,8 @@ function valid(id: string) {
o.reply = "The launch profile returned Unpredictable launch result.";
} else if (id === "assistant-connections.device-login") {
Object.assign(o.state, { verificationPath: "/mcp-device", currentCodeVisible: true, companyConsentVisible: true, readScopeVisible: true, humanApproved: true,
approvalUi: { processIsolation: true, testRunnerAttached: false, tracingEnabled: false, videoEnabled: false, apiStepRecorder: false, deviceSessionVerified: true, currentCodeVisible: true, companyConsentVisible: true, readScopeVisible: true, humanApproved: true },
expiredUi: { processIsolation: true, testRunnerAttached: false, tracingEnabled: false, videoEnabled: false, apiStepRecorder: false, deviceSessionVerified: true, expiredPageVisible: true },
clientCompanyId: "company", clientReadOnly: true, pendingError: "authorization_pending", wrongResourceDenied: true, wrongClientDenied: true,
correctExchangeSucceeded: true, reusedCodeDenied: true, expirationWaitMs: 600000, elapsedBeforeExpiryProbe: 600501, expiredCodeDenied: true, expiredPageVisible: true });
o.reply = "Open the verification page and sign in to approve the CLI device code as yourself.";