Redact current user in comments and token checks

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-03-11 22:17:21 -05:00
parent b1bf09970f
commit 088eaea0cb
6 changed files with 225 additions and 55 deletions

View File

@@ -1,6 +1,7 @@
import type { Db } from "@paperclipai/db";
import { activityLog } from "@paperclipai/db";
import { publishLiveEvent } from "./live-events.js";
import { redactCurrentUserValue } from "../log-redaction.js";
import { sanitizeRecord } from "../redaction.js";
export interface LogActivityInput {
@@ -17,6 +18,7 @@ export interface LogActivityInput {
export async function logActivity(db: Db, input: LogActivityInput) {
const sanitizedDetails = input.details ? sanitizeRecord(input.details) : null;
const redactedDetails = sanitizedDetails ? redactCurrentUserValue(sanitizedDetails) : null;
await db.insert(activityLog).values({
companyId: input.companyId,
actorType: input.actorType,
@@ -26,7 +28,7 @@ export async function logActivity(db: Db, input: LogActivityInput) {
entityId: input.entityId,
agentId: input.agentId ?? null,
runId: input.runId ?? null,
details: sanitizedDetails,
details: redactedDetails,
});
publishLiveEvent({
@@ -40,7 +42,7 @@ export async function logActivity(db: Db, input: LogActivityInput) {
entityId: input.entityId,
agentId: input.agentId ?? null,
runId: input.runId ?? null,
details: sanitizedDetails,
details: redactedDetails,
},
});
}

View File

@@ -2,9 +2,17 @@ import { and, asc, eq, inArray } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import { approvalComments, approvals } from "@paperclipai/db";
import { notFound, unprocessable } from "../errors.js";
import { redactCurrentUserText } from "../log-redaction.js";
import { agentService } from "./agents.js";
import { notifyHireApproved } from "./hire-hook.js";
function redactApprovalComment<T extends { body: string }>(comment: T): T {
return {
...comment,
body: redactCurrentUserText(comment.body),
};
}
export function approvalService(db: Db) {
const agentsSvc = agentService(db);
const canResolveStatuses = new Set(["pending", "revision_requested"]);
@@ -215,7 +223,8 @@ export function approvalService(db: Db) {
eq(approvalComments.companyId, existing.companyId),
),
)
.orderBy(asc(approvalComments.createdAt));
.orderBy(asc(approvalComments.createdAt))
.then((comments) => comments.map(redactApprovalComment));
},
addComment: async (
@@ -224,6 +233,7 @@ export function approvalService(db: Db) {
actor: { agentId?: string; userId?: string },
) => {
const existing = await getExistingApproval(approvalId);
const redactedBody = redactCurrentUserText(body);
return db
.insert(approvalComments)
.values({
@@ -231,10 +241,10 @@ export function approvalService(db: Db) {
approvalId,
authorAgentId: actor.agentId ?? null,
authorUserId: actor.userId ?? null,
body,
body: redactedBody,
})
.returning()
.then((rows) => rows[0]);
.then((rows) => redactApprovalComment(rows[0]));
},
};
}

View File

@@ -22,6 +22,7 @@ import {
defaultIssueExecutionWorkspaceSettingsForProject,
parseProjectExecutionWorkspacePolicy,
} from "./execution-workspace-policy.js";
import { redactCurrentUserText } from "../log-redaction.js";
const ALL_ISSUE_STATUSES = ["backlog", "todo", "in_progress", "in_review", "blocked", "done", "cancelled"];
@@ -88,6 +89,13 @@ type IssueUserContextInput = {
updatedAt: Date | string;
};
function redactIssueComment<T extends { body: string }>(comment: T): T {
return {
...comment,
body: redactCurrentUserText(comment.body),
};
}
function sameRunLock(checkoutRunId: string | null, actorRunId: string | null) {
if (actorRunId) return checkoutRunId === actorRunId;
return checkoutRunId == null;
@@ -1041,14 +1049,18 @@ export function issueService(db: Db) {
.select()
.from(issueComments)
.where(eq(issueComments.issueId, issueId))
.orderBy(desc(issueComments.createdAt)),
.orderBy(desc(issueComments.createdAt))
.then((comments) => comments.map(redactIssueComment)),
getComment: (commentId: string) =>
db
.select()
.from(issueComments)
.where(eq(issueComments.id, commentId))
.then((rows) => rows[0] ?? null),
.then((rows) => {
const comment = rows[0] ?? null;
return comment ? redactIssueComment(comment) : null;
}),
addComment: async (issueId: string, body: string, actor: { agentId?: string; userId?: string }) => {
const issue = await db
@@ -1059,6 +1071,7 @@ export function issueService(db: Db) {
if (!issue) throw notFound("Issue not found");
const redactedBody = redactCurrentUserText(body);
const [comment] = await db
.insert(issueComments)
.values({
@@ -1066,7 +1079,7 @@ export function issueService(db: Db) {
issueId,
authorAgentId: actor.agentId ?? null,
authorUserId: actor.userId ?? null,
body,
body: redactedBody,
})
.returning();
@@ -1076,7 +1089,7 @@ export function issueService(db: Db) {
.set({ updatedAt: new Date() })
.where(eq(issues.id, issueId));
return comment;
return redactIssueComment(comment);
},
createAttachment: async (input: {