mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 09:41:04 +01:00
Verify private task denial contracts with current master
Preserve existing PR history and verified downstream privacy rules. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
commit
7b0755dac2
5 files changed
+14
-13
No files matched your search
@@ -7571,14 +7571,6 @@ it("preserves prepared input and completion feedback through runnerd and the rea
|
||||
`], { maxBuffer: 16 * 1024 * 1024 });
|
||||
await writeFile(proxy, proxyBytes, { mode: 0o755 });
|
||||
const digest = (file: string) => `sha256:${createHash("sha256").update(readFileSync(file)).digest("hex")}`;
|
||||
// Linux runner images can install Node with group-write permission. Qualify
|
||||
// an isolated copy without mutating the host. Keep the original on macOS,
|
||||
// where Homebrew Node can load dylibs relative to its installation directory.
|
||||
const providerNode = process.platform === "linux" ? join(root, "node") : process.execPath;
|
||||
if (providerNode !== process.execPath) {
|
||||
await cp(process.execPath, providerNode);
|
||||
await chmod(providerNode, 0o755);
|
||||
}
|
||||
const runtime = join(root, "opencode");
|
||||
const bundle = createCapabilityRunnerdCodexTransport({
|
||||
provider: "opencode",
|
||||
|
||||
@@ -190,8 +190,9 @@ const support = await getEmbeddedPostgresTestSupport();
|
||||
{ projectId: f.projectId, assigneeAgentId: null, assigneeUserId: "board-user" },
|
||||
]) {
|
||||
const result = await create(path, fields);
|
||||
expect(result.status, JSON.stringify(result.body)).toBe(403);
|
||||
expect(result.body.error).toMatch(/outside.*boundary|cannot assign work to board users|different company/i);
|
||||
const hiddenProject = fields.projectId !== f.projectId && path.startsWith("/api/companies/");
|
||||
expect(result.status, JSON.stringify(result.body)).toBe(hiddenProject ? 404 : 403);
|
||||
expect(result.body.error).toMatch(hiddenProject ? /^Project not found$/ : /outside.*boundary|cannot assign work to board users|different company/i);
|
||||
}
|
||||
}
|
||||
const after = await server.db.select({ id: issues.id }).from(issues).where(eq(issues.companyId, f.companyId));
|
||||
@@ -232,7 +233,10 @@ const support = await getEmbeddedPostgresTestSupport();
|
||||
body: JSON.stringify({ title: "Root cannot escape into another project", parentId: f.issueId,
|
||||
projectId: outside.id, assigneeAgentId: f.agentId, status: "backlog" }),
|
||||
});
|
||||
expect(response.status, JSON.stringify(await response.json())).toBe(403);
|
||||
const denied = await response.json();
|
||||
const hiddenProject = path.startsWith("/api/companies/");
|
||||
expect(response.status, JSON.stringify(denied)).toBe(hiddenProject ? 404 : 403);
|
||||
expect(denied.error).toMatch(hiddenProject ? /^Project not found$/ : /outside.*boundary/i);
|
||||
}
|
||||
await expect(call(f, "create_task", { title: "Native root cannot escape", projectId: outside.id,
|
||||
idempotencyKey: "root-outside", status: "backlog" })).rejects.toThrow(/outside.*boundary/);
|
||||
|
||||
@@ -1734,7 +1734,7 @@ describeEmbeddedPostgres(
|
||||
const before = await snapshot(db);
|
||||
const res = await attempt.req();
|
||||
expect(res.status, `${attempt.id}: ${JSON.stringify(res.body)}`).toBe(
|
||||
["LT approvals", "LT-15/16", "LT-19", "LT-26 child", "LT-26 company issue", "LT-26 interaction", "LT-06 resume", "LT-06 blocker mutation"].includes(attempt.id) ? 403 : 404,
|
||||
["LT approvals", "LT-15/16", "LT-19", "LT-26 child", "LT-26 child with unauthorized assignee", "LT-26 company issue", "LT-26 interaction", "LT-06 resume", "LT-06 blocker mutation"].includes(attempt.id) ? 403 : 404,
|
||||
);
|
||||
expectNoCanary(res.body, ...forbiddenMarkers);
|
||||
const after = await snapshot(db);
|
||||
|
||||
@@ -265,6 +265,7 @@ describeEmbeddedPostgres("gateway connector invocation telemetry", () => {
|
||||
}, 30_000);
|
||||
|
||||
afterEach(async () => {
|
||||
vi.useRealTimers();
|
||||
track.mockClear();
|
||||
failLogActivityForAction.current = null;
|
||||
await db.delete(activityLog);
|
||||
@@ -379,6 +380,9 @@ describeEmbeddedPostgres("gateway connector invocation telemetry", () => {
|
||||
}, 30_000);
|
||||
|
||||
it("emits one denied event for a policy block and one rate_limited event past the limit", async () => {
|
||||
// Both calls must land in the same policy window, even across a minute boundary.
|
||||
vi.useFakeTimers({ toFake: ["Date"] });
|
||||
vi.setSystemTime(new Date("2026-10-06T12:00:30Z"));
|
||||
const f = await fixture();
|
||||
await db.insert(toolPolicies).values({
|
||||
companyId: f.company.id,
|
||||
|
||||
@@ -508,7 +508,8 @@ describe("AI repair inside the card", () => {
|
||||
setupOptionsMock.mockResolvedValue({ interaction, existingConnections: [], aiConnection: binding, aiConnectionRequiresAdoption: true });
|
||||
if (fails) adoptMock.mockRejectedValue(new Error("Connection test failed"));
|
||||
else adoptMock.mockResolvedValue({ ...interaction, status: "accepted" });
|
||||
renderBody(interaction); await flush();
|
||||
renderBody(interaction);
|
||||
await waitForAssertion(() => expect(button("Connect OpenAI")).toBeDefined());
|
||||
await act(() => button("Connect OpenAI")!.click());
|
||||
await act(() => button("Reconnect selected account")!.click());
|
||||
expect(adoptMock).not.toHaveBeenCalled();
|
||||
|
||||
Reference in new issue
Block a user