mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
test(e2e): accept explicit broad ChatGPT write consent
This commit is contained in:
1 parent
b349eb24c4
commit
5c9eaa8ee6
5 files changed
+84
-18
No files matched your search
@@ -46,6 +46,7 @@ export interface ChatgptReceipt {
|
||||
requestedScopes?: string[];
|
||||
acceptedScopes?: string[];
|
||||
configureDisclosed?: boolean;
|
||||
consentWording?: string;
|
||||
}
|
||||
const uuid = (s: unknown) =>
|
||||
typeof s === "string" &&
|
||||
@@ -135,6 +136,7 @@ export function validChatgptReceipt(r: ChatgptReceipt, i: ChatgptInput) {
|
||||
"requestedScopes",
|
||||
"acceptedScopes",
|
||||
"configureDisclosed",
|
||||
"consentWording",
|
||||
];
|
||||
return (
|
||||
!!r &&
|
||||
@@ -162,6 +164,11 @@ export function validChatgptReceipt(r: ChatgptReceipt, i: ChatgptInput) {
|
||||
r.actualProviderCallback === true &&
|
||||
r.boundMcpComment === true &&
|
||||
r.zeroNativeAdmissions === true &&
|
||||
chatgptConsentDisclosure(
|
||||
r.requestedScopes?.includes("paperclip:configure") === true,
|
||||
r.consentWording,
|
||||
r.configureDisclosed,
|
||||
) &&
|
||||
exactAcceptedChatgptScopes(
|
||||
r.requestedScopes,
|
||||
r.acceptedScopes,
|
||||
@@ -224,3 +231,18 @@ export function exactAcceptedChatgptScopes(
|
||||
(!requested.includes("paperclip:configure") || configureDisclosed === true)
|
||||
);
|
||||
}
|
||||
|
||||
/** Broad actual write consent is supported; configuration acceptance also needs
|
||||
* the selected person's explicit private acknowledgement of requested authority. */
|
||||
export function chatgptConsentDisclosure(
|
||||
requestedConfigure: boolean,
|
||||
wording: unknown,
|
||||
acknowledged: unknown,
|
||||
) {
|
||||
return (
|
||||
typeof wording === "string" &&
|
||||
wording.trim().replace(/\s+/g, " ") ===
|
||||
"Write all of your Paperclip data" &&
|
||||
(!requestedConfigure || acknowledged === true)
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import {
|
||||
namedChatgptRequest,
|
||||
exactAcceptedChatgptScopes,
|
||||
chatgptConsentDisclosure,
|
||||
validChatgptInput,
|
||||
validChatgptReceipt,
|
||||
createdChatgptAppId,
|
||||
@@ -79,6 +80,7 @@ function receipt(): ChatgptReceipt {
|
||||
requestedScopes: [...scopes],
|
||||
acceptedScopes: [...scopes],
|
||||
configureDisclosed: true,
|
||||
consentWording: "Write all of your Paperclip data",
|
||||
};
|
||||
}
|
||||
function valid(): ExternalObservation {
|
||||
@@ -308,6 +310,24 @@ describe("finite private named-provider contract", () => {
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
it("accepts actual broad write wording plus explicit requested configure acknowledgement", () => {
|
||||
expect(
|
||||
chatgptConsentDisclosure(true, "Write all of your Paperclip data", true),
|
||||
).toBe(true);
|
||||
expect(
|
||||
chatgptConsentDisclosure(true, "Write all of your Paperclip data", false),
|
||||
).toBe(false);
|
||||
expect(
|
||||
chatgptConsentDisclosure(true, "Read all of your Paperclip data", true),
|
||||
).toBe(false);
|
||||
expect(
|
||||
chatgptConsentDisclosure(
|
||||
false,
|
||||
"Write all of your Paperclip data",
|
||||
false,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
it("requires actual named official callback/person/company request", () => {
|
||||
const r = {
|
||||
id: "request",
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import {
|
||||
setupCheckComment,
|
||||
exactAcceptedChatgptScopes,
|
||||
chatgptConsentDisclosure,
|
||||
type ChatgptReceipt,
|
||||
} from "./external-chatgpt-contract.js";
|
||||
import { waitForReply } from "./identity-support.js";
|
||||
@@ -112,7 +113,12 @@ export function gradeChatgptSetup(value: unknown) {
|
||||
r.realHumanConsent === true &&
|
||||
r.actualProviderCallback === true &&
|
||||
r.boundMcpComment === true &&
|
||||
r.zeroNativeAdmissions === true,
|
||||
r.zeroNativeAdmissions === true &&
|
||||
chatgptConsentDisclosure(
|
||||
r.requestedScopes?.includes("paperclip:configure") === true,
|
||||
r.consentWording,
|
||||
r.configureDisclosed,
|
||||
),
|
||||
"The selected human really uses ChatGPT's private custom-app UI and submits the actual Paperclip OAuth consent; an operator acknowledgement or generic MCP client alone is insufficient.",
|
||||
);
|
||||
|
||||
@@ -428,6 +434,8 @@ export const chatgptSetupCase = definition(
|
||||
actualNamedUi: true,
|
||||
actualHumanConsent: true,
|
||||
zeroNativeAdmissions: state.zeroNativeAdmissions,
|
||||
consentWording: receipt.consentWording,
|
||||
acceptedScopes: receipt.acceptedScopes,
|
||||
});
|
||||
await ctx.input.page.goto(source, { waitUntil: "domcontentloaded" });
|
||||
},
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
setupCheckComment,
|
||||
createdChatgptAppId,
|
||||
exactAcceptedChatgptScopes,
|
||||
chatgptConsentDisclosure,
|
||||
type ChatgptInput,
|
||||
type ChatgptReceipt,
|
||||
} from "./external-chatgpt-contract.js";
|
||||
@@ -320,14 +321,32 @@ async function main() {
|
||||
throw new Error(
|
||||
"Actual ChatGPT client/company/write request does not match qualification",
|
||||
);
|
||||
const configureDisclosed =
|
||||
!request.requestedConfigure ||
|
||||
(await consent
|
||||
.getByText(/configure agents, projects (?:and|&) skills/i)
|
||||
.isVisible());
|
||||
const consentLabel = consent.getByText("Write all of your Paperclip data", {
|
||||
exact: true,
|
||||
});
|
||||
await consentLabel.waitFor({ state: "visible" });
|
||||
const consentWording = await consentLabel.innerText();
|
||||
await pane(
|
||||
operator,
|
||||
"Review and approve the actual Paperclip consent",
|
||||
identity +
|
||||
`<p>On the real Paperclip consent tab, select this exact company and explicitly approve read and write access for the single controlled qualification below. ${request.requestedConfigure ? "Configuration authority is also requested. The actual broad Write all of your Paperclip data choice includes configuring agents, projects and skills in this isolated company; explicitly acknowledge that authority here, then knowingly accept the real broad write choice on the actual consent tab." : "No configuration authority is requested or accepted."} Never approve agent/impersonation scope. Click Connect organization THERE, then finish creating the private ChatGPT app. This panel never approves consent.</p>`,
|
||||
request.requestedConfigure
|
||||
? [
|
||||
"I explicitly accept the requested configuration authority for this isolated company under the real broad write consent choice.",
|
||||
]
|
||||
: [],
|
||||
"I reviewed the real consent tab",
|
||||
);
|
||||
await confirm(operator, 120000);
|
||||
const configureDisclosed = chatgptConsentDisclosure(
|
||||
request.requestedConfigure === true,
|
||||
consentWording,
|
||||
true,
|
||||
);
|
||||
if (!configureDisclosed)
|
||||
throw new Error(
|
||||
"Actual requested configure authority is not visibly disclosed on Paperclip consent",
|
||||
"Actual broad write consent and explicit requested configuration acceptance were not established",
|
||||
);
|
||||
const submitted = consent.waitForResponse(
|
||||
(r) =>
|
||||
@@ -337,14 +356,6 @@ async function main() {
|
||||
r.request().method() === "POST",
|
||||
{ timeout: 120000 },
|
||||
);
|
||||
await pane(
|
||||
operator,
|
||||
"Review and approve the actual Paperclip consent",
|
||||
identity +
|
||||
`<p>On the real Paperclip consent tab, select this exact company and explicitly approve read and write access for the single controlled qualification below. ${request.requestedConfigure ? "Configuration authority is also requested: explicitly review the real visible Configure agents, projects and skills disclosure before accepting it for this isolated company." : "No configuration authority is requested or accepted."} Never approve agent/impersonation scope. Click Connect organization THERE, then finish creating the private ChatGPT app. This panel never approves consent.</p>`,
|
||||
[],
|
||||
"I reviewed the real consent tab",
|
||||
);
|
||||
await consent.bringToFront();
|
||||
const response = await submitted,
|
||||
payload = response.request().postDataJSON() as {
|
||||
@@ -523,6 +534,7 @@ async function main() {
|
||||
],
|
||||
acceptedScopes: [...grant.scopes],
|
||||
configureDisclosed,
|
||||
consentWording,
|
||||
};
|
||||
await writeFile(
|
||||
path.join(directory, "qualified.json"),
|
||||
|
||||
@@ -335,9 +335,13 @@ availability. After the unchanged canonical CEO query, that person actually uses
|
||||
ChatGPT's supported private custom MCP app UI and the real Paperclip consent tab.
|
||||
The child observes the actual named-client request, official ChatGPT callback,
|
||||
explicit company and scope acceptance, and one new saved grant. Configuration
|
||||
scope is permitted only when requested, visibly disclosed in the real consent,
|
||||
and explicitly accepted; agent authority is excluded. The current source's
|
||||
write-only consent label does not itself establish configuration disclosure.
|
||||
scope is permitted only when actually requested and explicitly accepted by the
|
||||
authorized person under the real broad **Write all of your Paperclip data**
|
||||
choice. The private prompt separately requires that person's explicit
|
||||
acknowledgement of the requested configuration authority; it does not require
|
||||
an invented separate picker or literal agents/projects/skills enumeration on
|
||||
the product consent page. Actual visible consent wording and the exact scope
|
||||
intersection are recorded; agent authority is excluded.
|
||||
Changed or missing controls and failed provider authorization are nonpassing
|
||||
observations, never grounds for a generic-client substitution.
|
||||
|
||||
|
||||
Reference in new issue
Block a user